diff options
133 files changed, 8343 insertions, 872 deletions
diff --git a/.gitignore b/.gitignore index 0d20b6487c..685e379850 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +*.iml *.pyc +.idea/ diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 0473bb833e..a686dfb335 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -6,6 +6,7 @@ clang_format = true clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp cmds/idlcli/ include/input/ + include/powermanager/ libs/binder/fuzzer/ libs/binder/ndk/ libs/binderthreadstate/ @@ -18,7 +19,9 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp opengl/libs/ services/bufferhub/ services/inputflinger/ + services/powermanager/ services/surfaceflinger/ + services/vibratorservice/ services/vr/ vulkan/ diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp index 402767a426..64bfdf9289 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -37,10 +37,16 @@ cc_library { defaults: ["idlcli-defaults"], srcs: [ "CommandVibrator.cpp", + "vibrator/CommandAlwaysOnDisable.cpp", + "vibrator/CommandAlwaysOnEnable.cpp", "vibrator/CommandCompose.cpp", "vibrator/CommandGetCapabilities.cpp", "vibrator/CommandGetCompositionDelayMax.cpp", "vibrator/CommandGetCompositionSizeMax.cpp", + "vibrator/CommandGetPrimitiveDuration.cpp", + "vibrator/CommandGetSupportedAlwaysOnEffects.cpp", + "vibrator/CommandGetSupportedEffects.cpp", + "vibrator/CommandGetSupportedPrimitives.cpp", "vibrator/CommandOff.cpp", "vibrator/CommandOn.cpp", "vibrator/CommandPerform.cpp", diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h index a8e595470d..b8744555e2 100644 --- a/cmds/idlcli/utils.h +++ b/cmds/idlcli/utils.h @@ -17,6 +17,7 @@ #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ #define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ +#include <android/binder_enums.h> #include <hidl/HidlSupport.h> #include <iomanip> @@ -66,7 +67,7 @@ inline std::istream &operator>>(std::istream &stream, uint8_t &out) { } // namespace overrides -template <typename T, typename R = hardware::hidl_enum_range<T>> +template <typename T, typename R = ndk::enum_range<T>> inline std::istream &operator>>(std::istream &stream, T &out) { using overrides::operator>>; auto validRange = R(); diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h index ca5142dee9..6c30a9e2ca 100644 --- a/cmds/idlcli/vibrator.h +++ b/cmds/idlcli/vibrator.h @@ -16,8 +16,12 @@ #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_ #define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_ +#include <future> + +#include <aidl/android/hardware/vibrator/BnVibratorCallback.h> #include <aidl/android/hardware/vibrator/IVibrator.h> #include <android/binder_manager.h> +#include <android/binder_process.h> #include <android/hardware/vibrator/1.3/IVibrator.h> #include "utils.h" @@ -101,6 +105,18 @@ namespace V1_2 = ::android::hardware::vibrator::V1_2; namespace V1_3 = ::android::hardware::vibrator::V1_3; namespace aidl = ::aidl::android::hardware::vibrator; +class VibratorCallback : public aidl::BnVibratorCallback { +public: + ndk::ScopedAStatus onComplete() override { + mPromise.set_value(); + return ndk::ScopedAStatus::ok(); + } + void waitForComplete() { mPromise.get_future().wait(); } + +private: + std::promise<void> mPromise; +}; + } // namespace vibrator } // namespace idlcli diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp new file mode 100644 index 0000000000..9afa300c2b --- /dev/null +++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandAlwaysOnDisable : public Command { + std::string getDescription() const override { return "Disarm always-on haptic source."; } + + std::string getUsageSummary() const override { return "<id>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<id>", {"Source ID (device-specific)."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto id = args.pop<decltype(mId)>()) { + mId = *id; + std::cout << "Source ID: " << mId << std::endl; + } else { + std::cerr << "Missing or Invalid Source ID!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId); + + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } + + int32_t mId; +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnDisable>("alwaysOnDisable"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp new file mode 100644 index 0000000000..bb7f9f284a --- /dev/null +++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +using aidl::Effect; +using aidl::EffectStrength; + +class CommandAlwaysOnEnable : public Command { + std::string getDescription() const override { + return "Arm always-on haptic source with an effect."; + } + + std::string getUsageSummary() const override { return "<id> <effect> <strength>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<id>", {"Source ID (device-specific)."}}, + {"<effect>", {"Effect ID."}}, + {"<strength>", {"0-2."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto id = args.pop<decltype(mId)>()) { + mId = *id; + std::cout << "Source ID: " << mId << std::endl; + } else { + std::cerr << "Missing or Invalid Source ID!" << std::endl; + return USAGE; + } + if (auto effect = args.pop<decltype(mEffect)>()) { + mEffect = *effect; + std::cout << "Effect: " << toString(mEffect) << std::endl; + } else { + std::cerr << "Missing or Invalid Effect!" << std::endl; + return USAGE; + } + if (auto strength = args.pop<decltype(mStrength)>()) { + mStrength = *strength; + std::cout << "Strength: " << toString(mStrength) << std::endl; + } else { + std::cerr << "Missing or Invalid Strength!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength); + + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } + + int32_t mId; + Effect mEffect; + EffectStrength mStrength; +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnEnable>("alwaysOnEnable"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp index 4721a5f9ae..97c057fa0c 100644 --- a/cmds/idlcli/vibrator/CommandCompose.cpp +++ b/cmds/idlcli/vibrator/CommandCompose.cpp @@ -28,10 +28,13 @@ using aidl::CompositeEffect; class CommandCompose : public Command { std::string getDescription() const override { return "Compose vibration."; } - std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; } + std::string getUsageSummary() const override { + return "[options] <delay> <primitive> <scale> ..."; + } UsageDetails getUsageDetails() const override { UsageDetails details{ + {"-b", {"Block for duration of vibration."}}, {"<delay>", {"In milliseconds"}}, {"<primitive>", {"Primitive ID."}}, {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}}, @@ -41,6 +44,17 @@ class CommandCompose : public Command { } Status doArgs(Args &args) override { + while (args.get<std::string>().value_or("").find("-") == 0) { + auto opt = *args.pop<std::string>(); + if (opt == "--") { + break; + } else if (opt == "-b") { + mBlocking = true; + } else { + std::cerr << "Invalid Option '" << opt << "'!" << std::endl; + return USAGE; + } + } while (!args.empty()) { CompositeEffect effect; if (auto delay = args.pop<decltype(effect.delayMs)>()) { @@ -50,9 +64,8 @@ class CommandCompose : public Command { std::cerr << "Missing or Invalid Delay!" << std::endl; return USAGE; } - // TODO: Use range validation when supported by AIDL - if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) { - effect.primitive = static_cast<decltype(effect.primitive)>(*primitive); + if (auto primitive = args.pop<decltype(effect.primitive)>()) { + effect.primitive = *primitive; std::cout << "Primitive: " << toString(effect.primitive) << std::endl; } else { std::cerr << "Missing or Invalid Primitive!" << std::endl; @@ -76,21 +89,33 @@ class CommandCompose : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - Status ret; - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + auto hal = getHal<aidl::IVibrator>(); + + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + std::shared_ptr<VibratorCallback> callback; + + if (mBlocking) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); + } + + auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback); + + if (status.isOk() && callback) { + callback->waitForComplete(); + } + + std::cout << "Status: " << status.getDescription() << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } + bool mBlocking; std::vector<CompositeEffect> mComposite; }; diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp new file mode 100644 index 0000000000..460d39e64f --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <future> + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +using aidl::CompositePrimitive; + +class CommandGetPrimitiveDuration : public Command { + std::string getDescription() const override { + return "Retrieve effect primitive's duration in milliseconds."; + } + + std::string getUsageSummary() const override { return "<primitive>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<primitive>", {"Primitive ID."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto primitive = args.pop<decltype(mPrimitive)>()) { + mPrimitive = *primitive; + std::cout << "Primitive: " << toString(mPrimitive) << std::endl; + } else { + std::cerr << "Missing or Invalid Primitive!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + int32_t duration; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Duration: " << duration << std::endl; + + return ret; + } + + CompositePrimitive mPrimitive; +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetPrimitiveDuration>( + "getPrimitiveDuration"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp new file mode 100644 index 0000000000..edfcd9195a --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +using aidl::Effect; + +class CommandGetSupportedAlwaysOnEffects : public Command { + std::string getDescription() const override { return "List of supported always-on effects."; } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + std::vector<Effect> effects; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Effects:" << std::endl; + for (auto &e : effects) { + std::cout << " " << toString(e) << std::endl; + } + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetSupportedAlwaysOnEffects>( + "getSupportedAlwaysOnEffects"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp new file mode 100644 index 0000000000..7658f22def --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +using aidl::Effect; + +class CommandGetSupportedEffects : public Command { + std::string getDescription() const override { return "List supported effects."; } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + std::vector<Effect> effects; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Effects:" << std::endl; + for (auto &e : effects) { + std::cout << " " << toString(e) << std::endl; + } + + return ret; + } +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetSupportedEffects>( + "getSupportedEffects"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp new file mode 100644 index 0000000000..d101681914 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +using aidl::CompositePrimitive; + +class CommandGetSupportedPrimitives : public Command { + std::string getDescription() const override { return "List of supported effect primitive."; } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + std::vector<CompositePrimitive> primitives; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Primitives:" << std::endl; + for (auto &e : primitives) { + std::cout << " " << toString(e) << std::endl; + } + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetSupportedPrimitives>( + "getSupportedPrimitives"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp index 4e7e493d6d..8212fc14a7 100644 --- a/cmds/idlcli/vibrator/CommandOn.cpp +++ b/cmds/idlcli/vibrator/CommandOn.cpp @@ -13,9 +13,14 @@ * limitations under the License. */ +#include <thread> + #include "utils.h" #include "vibrator.h" +using std::chrono::milliseconds; +using std::this_thread::sleep_for; + namespace android { namespace idlcli { @@ -26,16 +31,28 @@ namespace vibrator { class CommandOn : public Command { std::string getDescription() const override { return "Turn on vibrator."; } - std::string getUsageSummary() const override { return "<duration>"; } + std::string getUsageSummary() const override { return "[options] <duration>"; } UsageDetails getUsageDetails() const override { UsageDetails details{ + {"-b", {"Block for duration of vibration."}}, {"<duration>", {"In milliseconds."}}, }; return details; } Status doArgs(Args &args) override { + while (args.get<std::string>().value_or("").find("-") == 0) { + auto opt = *args.pop<std::string>(); + if (opt == "--") { + break; + } else if (opt == "-b") { + mBlocking = true; + } else { + std::cerr << "Invalid Option '" << opt << "'!" << std::endl; + return USAGE; + } + } if (auto duration = args.pop<decltype(mDuration)>()) { mDuration = *duration; } else { @@ -52,9 +69,21 @@ class CommandOn : public Command { Status doMain(Args && /*args*/) override { std::string statusStr; Status ret; + std::shared_ptr<VibratorCallback> callback; if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + int32_t cap; + hal->call(&aidl::IVibrator::getCapabilities, &cap); + + if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); + } + + auto status = hal->call(&aidl::IVibrator::on, mDuration, callback); + statusStr = status.getDescription(); ret = status.isOk() ? OK : ERROR; } else if (auto hal = getHal<V1_0::IVibrator>()) { @@ -65,11 +94,20 @@ class CommandOn : public Command { return UNAVAILABLE; } + if (ret == OK && mBlocking) { + if (callback) { + callback->waitForComplete(); + } else { + sleep_for(milliseconds(mDuration)); + } + } + std::cout << "Status: " << statusStr << std::endl; return ret; } + bool mBlocking; uint32_t mDuration; }; diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp index 69c7e37744..c897686cbe 100644 --- a/cmds/idlcli/vibrator/CommandPerform.cpp +++ b/cmds/idlcli/vibrator/CommandPerform.cpp @@ -13,9 +13,14 @@ * limitations under the License. */ +#include <thread> + #include "utils.h" #include "vibrator.h" +using std::chrono::milliseconds; +using std::this_thread::sleep_for; + namespace android { namespace idlcli { @@ -51,16 +56,17 @@ static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); -using V1_0::EffectStrength; -using V1_3::Effect; +using aidl::Effect; +using aidl::EffectStrength; class CommandPerform : public Command { std::string getDescription() const override { return "Perform vibration effect."; } - std::string getUsageSummary() const override { return "<effect> <strength>"; } + std::string getUsageSummary() const override { return "[options] <effect> <strength>"; } UsageDetails getUsageDetails() const override { UsageDetails details{ + {"-b", {"Block for duration of vibration."}}, {"<effect>", {"Effect ID."}}, {"<strength>", {"0-2."}}, }; @@ -68,6 +74,17 @@ class CommandPerform : public Command { } Status doArgs(Args &args) override { + while (args.get<std::string>().value_or("").find("-") == 0) { + auto opt = *args.pop<std::string>(); + if (opt == "--") { + break; + } else if (opt == "-b") { + mBlocking = true; + } else { + std::cerr << "Invalid Option '" << opt << "'!" << std::endl; + return USAGE; + } + } if (auto effect = args.pop<decltype(mEffect)>()) { mEffect = *effect; std::cout << "Effect: " << toString(mEffect) << std::endl; @@ -93,12 +110,23 @@ class CommandPerform : public Command { std::string statusStr; uint32_t lengthMs; Status ret; + std::shared_ptr<VibratorCallback> callback; if (auto hal = getHal<aidl::IVibrator>()) { + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + int32_t cap; + hal->call(&aidl::IVibrator::getCapabilities, &cap); + + if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); + } + int32_t aidlLengthMs; - auto status = - hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect), - static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs); + auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback, + &aidlLengthMs); + statusStr = status.getDescription(); lengthMs = static_cast<uint32_t>(aidlLengthMs); ret = status.isOk() ? OK : ERROR; @@ -111,17 +139,20 @@ class CommandPerform : public Command { }; if (auto hal = getHal<V1_3::IVibrator>()) { - hidlRet = hal->call(&V1_3::IVibrator::perform_1_3, - static_cast<V1_3::Effect>(mEffect), mStrength, callback); + hidlRet = + hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect), + static_cast<V1_0::EffectStrength>(mStrength), callback); } else if (auto hal = getHal<V1_2::IVibrator>()) { - hidlRet = hal->call(&V1_2::IVibrator::perform_1_2, - static_cast<V1_2::Effect>(mEffect), mStrength, callback); + hidlRet = + hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect), + static_cast<V1_0::EffectStrength>(mStrength), callback); } else if (auto hal = getHal<V1_1::IVibrator>()) { hidlRet = hal->call(&V1_1::IVibrator::perform_1_1, - static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback); + static_cast<V1_1::Effect_1_1>(mEffect), + static_cast<V1_0::EffectStrength>(mStrength), callback); } else if (auto hal = getHal<V1_0::IVibrator>()) { hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect), - mStrength, callback); + static_cast<V1_0::EffectStrength>(mStrength), callback); } else { return UNAVAILABLE; } @@ -130,12 +161,21 @@ class CommandPerform : public Command { ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR; } + if (ret == OK && mBlocking) { + if (callback) { + callback->waitForComplete(); + } else { + sleep_for(milliseconds(lengthMs)); + } + } + std::cout << "Status: " << statusStr << std::endl; std::cout << "Length: " << lengthMs << std::endl; return ret; } + bool mBlocking; Effect mEffect; EffectStrength mStrength; }; diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 0782b430a3..e7014c8d0b 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -2246,7 +2246,7 @@ binder::Status InstalldNativeService::getAppCrates( #if CRATE_DEBUG LOG(WARNING) << "retVector.size() =" << retVector.size(); for (auto& item : retVector) { - CrateManager::dump(item); + CrateManager::dump(*item); } #endif @@ -2278,7 +2278,7 @@ binder::Status InstalldNativeService::getUserCrates( if (cratedFolder == nullptr) { return; } - retVector->push_back(std::move(crateMetadata)); + retVector.push_back(std::move(crateMetadata)); }; std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void { @@ -2290,7 +2290,7 @@ binder::Status InstalldNativeService::getUserCrates( #if CRATE_DEBUG LOG(DEBUG) << "retVector.size() =" << retVector.size(); for (auto& item : retVector) { - CrateManager::dump(item); + CrateManager::dump(*item); } #endif diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 4ac70a4857..eeda6c5855 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -117,10 +117,11 @@ interface IInstalld { int userId, int snapshotId, int storageFlags); void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags); - void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId, - in int[] retainSnapshotIds); void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, long ceSnapshotInode, int snapshotId, int storageFlags); + void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId, + in int[] retainSnapshotIds); + void tryMountDataMirror(@nullable @utf8InCpp String volumeUuid); void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid); diff --git a/include/android/input.h b/include/android/input.h index dbfd61eb05..7c392348b7 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -55,6 +55,7 @@ #include <sys/types.h> #include <android/keycodes.h> #include <android/looper.h> +#include <jni.h> #if !defined(__INTRODUCED_IN) #define __INTRODUCED_IN(__api_level) /* nothing */ @@ -931,6 +932,15 @@ int32_t AInputEvent_getDeviceId(const AInputEvent* event); /** Get the input event source. */ int32_t AInputEvent_getSource(const AInputEvent* event); +/** + * Releases interface objects created by {@link AKeyEvent_fromJava()} + * and {@link AMotionEvent_fromJava()}. + * After returning, the specified AInputEvent* object becomes invalid and should no longer be used. + * The underlying Java object remains valid and does not change its state. + */ + +void AInputEvent_release(const AInputEvent* event); + /*** Accessors for key events only. ***/ /** Get the key event action. */ @@ -977,6 +987,15 @@ int64_t AKeyEvent_getDownTime(const AInputEvent* key_event); */ int64_t AKeyEvent_getEventTime(const AInputEvent* key_event); +/** + * Creates a native AInputEvent* object associated with the specified Java android.view.KeyEvent. + * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. + * The object returned by this function must be disposed using {@link AInputEvent_release()}. + * User must guarantee that lifetime for object referenced by keyEvent is prolongated + * up to release of returned AInputEvent*. + */ +const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent); + /*** Accessors for motion events only. ***/ /** Get the combined motion event action code and pointer index. */ @@ -1292,6 +1311,14 @@ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, siz float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, int32_t axis, size_t pointer_index, size_t history_index); +/** + * Creates a native AInputEvent* object associated with the specified Java android.view.MotionEvent. + * The result may be used with generic and MotionEvent-specific AInputEvent_* functions. + * The object returned by this function must be disposed using {@link AInputEvent_release()}. + * User must guarantee that object referenced by motionEvent won't be recycled and + * its lifetime is prolongated up to release of returned AInputEvent*. + */ +const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent); struct AInputQueue; /** diff --git a/include/input/Input.h b/include/input/Input.h index 54b4e5a737..ac901ae88a 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -280,9 +280,9 @@ private: public: // Used to divide integer space to ensure no conflict among these sources./ enum class Source : int32_t { - INPUT_READER = 0x0 << SOURCE_SHIFT, - INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT, - OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events + INPUT_READER = static_cast<int32_t>(0x0u << SOURCE_SHIFT), + INPUT_DISPATCHER = static_cast<int32_t>(0x1u << SOURCE_SHIFT), + OTHER = static_cast<int32_t>(0x3u << SOURCE_SHIFT), // E.g. app injected events }; IdGenerator(Source source); @@ -294,7 +294,7 @@ public: private: const Source mSource; - static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT; + static constexpr int32_t SOURCE_MASK = static_cast<int32_t>(0x3u << SOURCE_SHIFT); }; /** diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index 2dac5b62a7..6740855791 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -37,23 +37,23 @@ struct InputWindowInfo { InputWindowInfo(const Parcel& from); // Window flags from WindowManager.LayoutParams - enum { - FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, - FLAG_DIM_BEHIND = 0x00000002, - FLAG_BLUR_BEHIND = 0x00000004, - FLAG_NOT_FOCUSABLE = 0x00000008, - FLAG_NOT_TOUCHABLE = 0x00000010, - FLAG_NOT_TOUCH_MODAL = 0x00000020, + enum : uint32_t { + FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, + FLAG_DIM_BEHIND = 0x00000002, + FLAG_BLUR_BEHIND = 0x00000004, + FLAG_NOT_FOCUSABLE = 0x00000008, + FLAG_NOT_TOUCHABLE = 0x00000010, + FLAG_NOT_TOUCH_MODAL = 0x00000020, FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, - FLAG_KEEP_SCREEN_ON = 0x00000080, - FLAG_LAYOUT_IN_SCREEN = 0x00000100, - FLAG_LAYOUT_NO_LIMITS = 0x00000200, - FLAG_FULLSCREEN = 0x00000400, - FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, - FLAG_DITHER = 0x00001000, - FLAG_SECURE = 0x00002000, - FLAG_SCALED = 0x00004000, - FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, + FLAG_KEEP_SCREEN_ON = 0x00000080, + FLAG_LAYOUT_IN_SCREEN = 0x00000100, + FLAG_LAYOUT_NO_LIMITS = 0x00000200, + FLAG_FULLSCREEN = 0x00000400, + FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, + FLAG_DITHER = 0x00001000, + FLAG_SECURE = 0x00002000, + FLAG_SCALED = 0x00004000, + FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, FLAG_LAYOUT_INSET_DECOR = 0x00010000, FLAG_ALT_FOCUSABLE_IM = 0x00020000, FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, @@ -62,7 +62,14 @@ struct InputWindowInfo { FLAG_TURN_SCREEN_ON = 0x00200000, FLAG_DISMISS_KEYGUARD = 0x00400000, FLAG_SPLIT_TOUCH = 0x00800000, + FLAG_HARDWARE_ACCELERATED = 0x01000000, + FLAG_LAYOUT_IN_OVERSCAN = 0x02000000, + FLAG_TRANSLUCENT_STATUS = 0x04000000, + FLAG_TRANSLUCENT_NAVIGATION = 0x08000000, + FLAG_LOCAL_FOCUS_MODE = 0x10000000, FLAG_SLIPPERY = 0x20000000, + FLAG_LAYOUT_ATTACHED_IN_DECOR = 0x40000000, + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000, }; // Window types from WindowManager.LayoutParams @@ -107,8 +114,8 @@ struct InputWindowInfo { TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27, TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, + TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39, TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, - TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42, LAST_SYSTEM_WINDOW = 2999, }; @@ -163,6 +170,12 @@ struct InputWindowInfo { bool hasFocus = false; bool hasWallpaper = false; bool paused = false; + /* This flag is set when the window is of a trusted type that is allowed to silently + * overlay other windows for the purpose of implementing the secure views feature. + * Trusted overlays, such as IME windows, can partly obscure other windows without causing + * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. + */ + bool trustedOverlay = false; int32_t ownerPid = -1; int32_t ownerUid = -1; int32_t inputFeatures = 0; @@ -175,23 +188,19 @@ struct InputWindowInfo { void addTouchableRegion(const Rect& region); bool touchableRegionContainsPoint(int32_t x, int32_t y) const; - bool frameContainsPoint(int32_t x, int32_t y) const; - /* Returns true if the window is of a trusted type that is allowed to silently - * overlay other windows for the purpose of implementing the secure views feature. - * Trusted overlays, such as IME windows, can partly obscure other windows without causing - * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. - */ - bool isTrustedOverlay() const; + bool frameContainsPoint(int32_t x, int32_t y) const; bool supportsSplitTouch() const; bool overlaps(const InputWindowInfo* other) const; status_t write(Parcel& output) const; + static InputWindowInfo read(const Parcel& from); }; +std::string inputWindowFlagsToString(uint32_t flags); /* * Handle for a window that can receive input. diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 727865a933..ee010a3856 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -18,8 +18,8 @@ #define _LIBINPUT_VELOCITY_TRACKER_H #include <input/Input.h> -#include <utils/Timers.h> #include <utils/BitSet.h> +#include <utils/Timers.h> namespace android { @@ -30,6 +30,22 @@ class VelocityTrackerStrategy; */ class VelocityTracker { public: + enum class Strategy : int32_t { + DEFAULT = -1, + MIN = 0, + IMPULSE = 0, + LSQ1 = 1, + LSQ2 = 2, + LSQ3 = 3, + WLSQ2_DELTA = 4, + WLSQ2_CENTRAL = 5, + WLSQ2_RECENT = 6, + INT1 = 7, + INT2 = 8, + LEGACY = 9, + MAX = LEGACY, + }; + struct Position { float x, y; }; @@ -62,8 +78,8 @@ public: }; // Creates a velocity tracker using the specified strategy. - // If strategy is NULL, uses the default strategy for the platform. - VelocityTracker(const char* strategy = nullptr); + // If strategy is not provided, uses the default strategy for the platform. + VelocityTracker(const Strategy strategy = Strategy::DEFAULT); ~VelocityTracker(); @@ -102,16 +118,21 @@ public: inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; } private: - static const char* DEFAULT_STRATEGY; + // The default velocity tracker strategy. + // Although other strategies are available for testing and comparison purposes, + // this is the strategy that applications will actually use. Be very careful + // when adjusting the default strategy because it can dramatically affect + // (often in a bad way) the user experience. + static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2; nsecs_t mLastEventTime; BitSet32 mCurrentPointerIdBits; int32_t mActivePointerId; - VelocityTrackerStrategy* mStrategy; + std::unique_ptr<VelocityTrackerStrategy> mStrategy; - bool configureStrategy(const char* strategy); + bool configureStrategy(const Strategy strategy); - static VelocityTrackerStrategy* createStrategy(const char* strategy); + static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy); }; diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h deleted file mode 100644 index 964e318584..0000000000 --- a/include/powermanager/IPowerManager.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_IPOWERMANAGER_H -#define ANDROID_IPOWERMANAGER_H - -#include <utils/Errors.h> -#include <binder/IInterface.h> -#include <hardware/power.h> - -namespace android { - -// ---------------------------------------------------------------------------- - -class IPowerManager : public IInterface -{ -public: - // These transaction IDs must be kept in sync with the method order from - // IPowerManager.aidl. - enum { - ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION, - ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1, - RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2, - UPDATE_WAKE_LOCK_UIDS = IBinder::FIRST_CALL_TRANSACTION + 3, - POWER_HINT = IBinder::FIRST_CALL_TRANSACTION + 4, - UPDATE_WAKE_LOCK_SOURCE = IBinder::FIRST_CALL_TRANSACTION + 5, - IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6, - USER_ACTIVITY = IBinder::FIRST_CALL_TRANSACTION + 7, - WAKE_UP = IBinder::FIRST_CALL_TRANSACTION + 8, - GO_TO_SLEEP = IBinder::FIRST_CALL_TRANSACTION + 9, - NAP = IBinder::FIRST_CALL_TRANSACTION + 10, - IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11, - IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12, - GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13, - SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14, - REBOOT = IBinder::FIRST_CALL_TRANSACTION + 21, - REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 22, - SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 23, - CRASH = IBinder::FIRST_CALL_TRANSACTION + 24, - }; - - DECLARE_META_INTERFACE(PowerManager) - - // The parcels created by these methods must be kept in sync with the - // corresponding methods from IPowerManager.aidl. - // FIXME remove the bool isOneWay parameters as they are not oneway in the .aidl - virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag, - const String16& packageName, bool isOneWay = false) = 0; - virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag, - const String16& packageName, int uid, bool isOneWay = false) = 0; - virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay = false) = 0; - virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids, - bool isOneWay = false) = 0; - virtual status_t powerHint(int hintId, int data) = 0; - virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0; - virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0; - virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0; - virtual status_t crash(const String16& message) = 0; -}; - -// ---------------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IPOWERMANAGER_H diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h new file mode 100644 index 0000000000..dd34c0a6c2 --- /dev/null +++ b/include/powermanager/PowerHalController.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_POWERHALCONTROLLER_H +#define ANDROID_POWERHALCONTROLLER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <powermanager/PowerHalWrapper.h> + +namespace android { + +namespace power { + +// ------------------------------------------------------------------------------------------------- + +// Connects to underlying Power HAL handles. +class HalConnector { +public: + HalConnector() = default; + virtual ~HalConnector() = default; + + virtual std::unique_ptr<HalWrapper> connect(); + virtual void reset(); +}; + +// ------------------------------------------------------------------------------------------------- + +// Controller for Power HAL handle. +// This relies on HalConnector to connect to the underlying Power HAL +// service and reconnects to it after each failed api call. This also ensures +// connecting to the service is thread-safe. +class PowerHalController : public HalWrapper { +public: + PowerHalController() : PowerHalController(std::make_unique<HalConnector>()) {} + explicit PowerHalController(std::unique_ptr<HalConnector> connector) + : mHalConnector(std::move(connector)) {} + virtual ~PowerHalController() = default; + + void init(); + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; + +private: + std::mutex mConnectedHalMutex; + std::unique_ptr<HalConnector> mHalConnector; + + // Shared pointers to keep global pointer and allow local copies to be used in + // different threads + std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr; + const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>(); + + std::shared_ptr<HalWrapper> initHal(); + HalResult processHalResult(HalResult result, const char* functionName); +}; + +// ------------------------------------------------------------------------------------------------- + +}; // namespace power + +}; // namespace android + +#endif // ANDROID_POWERHALCONTROLLER_H diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h new file mode 100644 index 0000000000..ed6f6f35f5 --- /dev/null +++ b/include/powermanager/PowerHalLoader.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_POWERHALLOADER_H +#define ANDROID_POWERHALLOADER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/IPower.h> + +namespace android { + +namespace power { + +// Loads available Power HAL services. +class PowerHalLoader { +public: + static void unloadAll(); + static sp<hardware::power::IPower> loadAidl(); + static sp<hardware::power::V1_0::IPower> loadHidlV1_0(); + static sp<hardware::power::V1_1::IPower> loadHidlV1_1(); + +private: + static std::mutex gHalMutex; + static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex); + static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex); + static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex); + + static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked() + EXCLUSIVE_LOCKS_REQUIRED(gHalMutex); + + PowerHalLoader() = delete; + ~PowerHalLoader() = delete; +}; + +}; // namespace power + +} // namespace android + +#endif // ANDROID_POWERHALLOADER_H diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h new file mode 100644 index 0000000000..c3e7601c29 --- /dev/null +++ b/include/powermanager/PowerHalWrapper.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_POWERHALWRAPPER_H +#define ANDROID_POWERHALWRAPPER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> + +namespace android { + +namespace power { + +// State of Power HAL support for individual apis. +enum class HalSupport { + UNKNOWN = 0, + ON = 1, + OFF = 2, +}; + +// State of the Power HAL api call result. +enum class HalResult { + SUCCESSFUL = 0, + FAILED = 1, + UNSUPPORTED = 2, +}; + +// Wrapper for Power HAL handlers. +class HalWrapper { +public: + virtual ~HalWrapper() = default; + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0; +}; + +// Empty Power HAL wrapper that ignores all api calls. +class EmptyHalWrapper : public HalWrapper { +public: + EmptyHalWrapper() = default; + ~EmptyHalWrapper() = default; + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; +}; + +// Wrapper for the HIDL Power HAL v1.0. +class HidlHalWrapperV1_0 : public HalWrapper { +public: + explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal) + : mHandleV1_0(std::move(Hal)) {} + virtual ~HidlHalWrapperV1_0() = default; + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; + +protected: + virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data); + +private: + sp<hardware::power::V1_0::IPower> mHandleV1_0; + HalResult setInteractive(bool enabled); + HalResult setFeature(hardware::power::V1_0::Feature feature, bool enabled); +}; + +// Wrapper for the HIDL Power HAL v1.1. +class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 { +public: + HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0, + sp<hardware::power::V1_1::IPower> handleV1_1) + : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {} + virtual ~HidlHalWrapperV1_1() = default; + +protected: + virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, + uint32_t data) override; + +private: + sp<hardware::power::V1_1::IPower> mHandleV1_1; +}; + +// Wrapper for the AIDL Power HAL. +class AidlHalWrapper : public HalWrapper { +public: + explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {} + virtual ~AidlHalWrapper() = default; + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; + +private: + // Control access to the boost and mode supported arrays. + std::mutex mBoostMutex; + std::mutex mModeMutex; + sp<hardware::power::IPower> mHandle; + // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT. + // Need to increase the array size if more boost supported. + std::array<std::atomic<HalSupport>, + static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1> + mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN}; + // Android framework only sends mode upto DISPLAY_INACTIVE. + // Need to increase the array if more mode supported. + std::array<std::atomic<HalSupport>, + static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1> + mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN}; +}; + +}; // namespace power + +}; // namespace android + +#endif // ANDROID_POWERHALWRAPPER_H diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp index 5e4c98fc7a..d005058abe 100644 --- a/libs/binder/ActivityManager.cpp +++ b/libs/binder/ActivityManager.cpp @@ -98,6 +98,15 @@ int32_t ActivityManager::getUidProcessState(const uid_t uid, const String16& cal return PROCESS_STATE_UNKNOWN; } +bool ActivityManager::setSchedPolicyCgroup(const int32_t tid, const int32_t group) +{ + sp<IActivityManager> service = getService(); + if (service != nullptr) { + return service->setSchedPolicyCgroup(tid, group); + } + return false; +} + status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<IActivityManager> service = getService(); if (service != nullptr) { diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index 1eb5363ae2..a30211225d 100644 --- a/libs/binder/IActivityManager.cpp +++ b/libs/binder/IActivityManager.cpp @@ -104,6 +104,17 @@ public: } return reply.readInt32(); } + + virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group) + { + Parcel data, reply; + data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); + data.writeInt32(tid); + data.writeInt32(group); + remote()->transact(SET_SCHED_POLICY_CGROUP_TRANSACTION, data, &reply); + if (reply.readExceptionCode() != 0) return false; + return reply.readBool(); + } }; // ------------------------------------------------------------------------------------ diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp index 52c730cfb8..8c0495cf54 100644 --- a/libs/binder/fuzzer/binder.cpp +++ b/libs/binder/fuzzer/binder.cpp @@ -135,6 +135,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16), PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16), + PARCEL_READ_WITH_STATUS(std::optional<std::string>, readUtf8FromUtf16), [] (const ::android::Parcel& p, uint8_t /*data*/) { FUZZ_LOG() << "about to read c-str"; const char* str = p.readCString(); @@ -143,6 +144,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_OPT_STATUS(android::String8, readString8), PARCEL_READ_OPT_STATUS(android::String16, readString16), PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16), + PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16), [] (const ::android::Parcel& p, uint8_t /*data*/) { FUZZ_LOG() << "about to readString16Inplace"; size_t outLen = 0; @@ -156,17 +158,22 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { // TODO(b/131868573): can force read of arbitrarily sized vector // PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector), // PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector), // PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector), // only reading one parcelable type for now // TODO(b/131868573): can force read of arbitrarily sized vector // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector), // PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector), PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable), PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable), + PARCEL_READ_WITH_STATUS(std::optional<ExampleParcelable>, readParcelable), // only reading one binder type for now PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder), @@ -174,30 +181,42 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { // TODO(b/131868573): can force read of arbitrarily sized vector // PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), + // PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), // PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector), // TODO(b/131868573): can force read of arbitrarily sized vector // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector), // PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector), // PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector), // PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector), // PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector), // PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector), // PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector), // PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector), // PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector), // PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector), // PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector), // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector), // PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector), [] (const android::Parcel& p, uint8_t /*len*/) { @@ -234,6 +253,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { // TODO(b/131868573): can force read of arbitrarily sized vector // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector), + // PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector), // PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector), [] (const android::Parcel& p, uint8_t len) { diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h index 9108e31758..7043b17b53 100644 --- a/libs/binder/include/binder/ActivityManager.h +++ b/libs/binder/include/binder/ActivityManager.h @@ -77,7 +77,7 @@ public: void unregisterUidObserver(const sp<IUidObserver>& observer); bool isUidActive(const uid_t uid, const String16& callingPackage); int getUidProcessState(const uid_t uid, const String16& callingPackage); - + bool setSchedPolicyCgroup(const int32_t tid, const int32_t group); status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h index e0248f6624..fe58a41546 100644 --- a/libs/binder/include/binder/IActivityManager.h +++ b/libs/binder/include/binder/IActivityManager.h @@ -39,13 +39,15 @@ public: virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0; virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0; virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0; + virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group) = 0; enum { OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_UID_OBSERVER_TRANSACTION, UNREGISTER_UID_OBSERVER_TRANSACTION, IS_UID_ACTIVE_TRANSACTION, - GET_UID_PROCESS_STATE_TRANSACTION + GET_UID_PROCESS_STATE_TRANSACTION, + SET_SCHED_POLICY_CGROUP_TRANSACTION }; }; diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 3d0f8bbb11..119b3e0b7d 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -29,7 +29,6 @@ #include <android-base/strings.h> #include <android/dlext.h> #include <binder/IServiceManager.h> -#include <cutils/properties.h> #include <graphicsenv/IGpuService.h> #include <log/log.h> #include <nativeloader/dlext_namespaces.h> @@ -74,7 +73,7 @@ static constexpr const char* kNativeLibrariesSystemConfigPath[] = static std::string vndkVersionStr() { #ifdef __BIONIC__ - return android::base::GetProperty("ro.vndk.version", ""); + return base::GetProperty("ro.vndk.version", ""); #endif return ""; } @@ -345,10 +344,8 @@ void* GraphicsEnv::loadLibrary(std::string name) { } bool GraphicsEnv::checkAngleRules(void* so) { - char manufacturer[PROPERTY_VALUE_MAX]; - char model[PROPERTY_VALUE_MAX]; - property_get("ro.product.manufacturer", manufacturer, "UNSET"); - property_get("ro.product.model", model, "UNSET"); + auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET"); + auto model = base::GetProperty("ro.product.model", "UNSET"); auto ANGLEGetFeatureSupportUtilAPIVersion = (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, @@ -401,7 +398,8 @@ bool GraphicsEnv::checkAngleRules(void* so) { ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); break; } - if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) { + if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(), + systemInfoHandle)) { ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); break; } @@ -653,8 +651,7 @@ android_namespace_t* GraphicsEnv::getAngleNamespace() { mAngleNamespace = android_create_namespace("ANGLE", nullptr, // ld_library_path mAnglePath.c_str(), // default_library_path - ANDROID_NAMESPACE_TYPE_SHARED | - ANDROID_NAMESPACE_TYPE_ISOLATED, + ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED, nullptr, // permitted_when_isolated_path nullptr); diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index e62a61fc55..6881be3e77 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -371,10 +371,8 @@ public: data.writeStrongBinder(display); remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply); const status_t result = reply.readInt32(); - if (result == NO_ERROR) { - memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo)); - } - return result; + if (result != NO_ERROR) return result; + return reply.read(*info); } virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) { @@ -1093,22 +1091,22 @@ public: return NO_ERROR; } - virtual status_t notifyPowerHint(int32_t hintId) { + virtual status_t notifyPowerBoost(int32_t boostId) { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to write interface token: %d", error); + ALOGE("notifyPowerBoost: failed to write interface token: %d", error); return error; } - error = data.writeInt32(hintId); + error = data.writeInt32(boostId); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to write hintId: %d", error); + ALOGE("notifyPowerBoost: failed to write boostId: %d", error); return error; } - error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply, + error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply, IBinder::FLAG_ONEWAY); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to transact: %d", error); + ALOGE("notifyPowerBoost: failed to transact: %d", error); return error; } return NO_ERROR; @@ -1442,10 +1440,8 @@ status_t BnSurfaceComposer::onTransact( const sp<IBinder> display = data.readStrongBinder(); const status_t result = getDisplayInfo(display, &info); reply->writeInt32(result); - if (result == NO_ERROR) { - memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo)); - } - return NO_ERROR; + if (result != NO_ERROR) return result; + return reply->write(info); } case GET_DISPLAY_CONFIGS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1989,15 +1985,15 @@ status_t BnSurfaceComposer::onTransact( } return setDisplayBrightness(displayToken, brightness); } - case NOTIFY_POWER_HINT: { + case NOTIFY_POWER_BOOST: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - int32_t hintId; - status_t error = data.readInt32(&hintId); + int32_t boostId; + status_t error = data.readInt32(&boostId); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to read hintId: %d", error); + ALOGE("notifyPowerBoost: failed to read boostId: %d", error); return error; } - return notifyPowerHint(hintId); + return notifyPowerBoost(boostId); } case SET_GLOBAL_SHADOW_SETTINGS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index cf269b33ba..a86eafaaef 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1493,7 +1493,7 @@ int Surface::dispatchGetLastQueuedBuffer(va_list args) { int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix); if (graphicBuffer != nullptr) { - *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()); + *buffer = graphicBuffer->toAHardwareBuffer(); AHardwareBuffer_acquire(*buffer); } else { *buffer = nullptr; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 83bc06997a..16b46df4f6 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1893,8 +1893,8 @@ status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayT return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness); } -status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) { - return ComposerService::getComposerService()->notifyPowerHint(hintId); +status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) { + return ComposerService::getComposerService()->notifyPowerBoost(boostId); } status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor, diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 8d3160a815..645714a915 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -496,14 +496,14 @@ public: virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0; /* - * Sends a power hint to the composer. This function is asynchronous. + * Sends a power boost to the composer. This function is asynchronous. * - * hintId - * hint id according to android::hardware::power::V1_0::PowerHint + * boostId + * boost id according to android::hardware::power::Boost * * Returns NO_ERROR upon success. */ - virtual status_t notifyPowerHint(int32_t hintId) = 0; + virtual status_t notifyPowerBoost(int32_t boostId) = 0; /* * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows @@ -591,7 +591,7 @@ public: GET_DISPLAY_BRIGHTNESS_SUPPORT, SET_DISPLAY_BRIGHTNESS, CAPTURE_SCREEN_BY_ID, - NOTIFY_POWER_HINT, + NOTIFY_POWER_BOOST, SET_GLOBAL_SHADOW_SETTINGS, GET_AUTO_LOW_LATENCY_MODE_SUPPORT, SET_AUTO_LOW_LATENCY_MODE, diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index adcb8982a0..eb757bad32 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -217,14 +217,14 @@ public: static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness); /* - * Sends a power hint to the composer. This function is asynchronous. + * Sends a power boost to the composer. This function is asynchronous. * - * hintId - * hint id according to android::hardware::power::V1_0::PowerHint + * boostId + * boost id according to android::hardware::power::Boost * * Returns NO_ERROR upon success. */ - static status_t notifyPowerHint(int32_t hintId); + static status_t notifyPowerBoost(int32_t boostId); /* * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 9906166c67..6d921432f7 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -850,7 +850,7 @@ public: float* /*outAppRequestRefreshRateMax*/) override { return NO_ERROR; }; - status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; } + status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; } status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/, float /*lightPosY*/, float /*lightPosZ*/, diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 85a2015e43..04550227e3 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -17,9 +17,10 @@ #define LOG_TAG "InputWindow" #define LOG_NDEBUG 0 +#include <android-base/stringprintf.h> #include <binder/Parcel.h> -#include <input/InputWindow.h> #include <input/InputTransport.h> +#include <input/InputWindow.h> #include <log/log.h> @@ -28,6 +29,118 @@ namespace android { +const char* inputWindowFlagToString(uint32_t flag) { + switch (flag) { + case InputWindowInfo::FLAG_ALLOW_LOCK_WHILE_SCREEN_ON: { + return "ALLOW_LOCK_WHILE_SCREEN_ON"; + } + case InputWindowInfo::FLAG_DIM_BEHIND: { + return "DIM_BEHIND"; + } + case InputWindowInfo::FLAG_BLUR_BEHIND: { + return "BLUR_BEHIND"; + } + case InputWindowInfo::FLAG_NOT_FOCUSABLE: { + return "NOT_FOCUSABLE"; + } + case InputWindowInfo::FLAG_NOT_TOUCHABLE: { + return "NOT_TOUCHABLE"; + } + case InputWindowInfo::FLAG_NOT_TOUCH_MODAL: { + return "NOT_TOUCH_MODAL"; + } + case InputWindowInfo::FLAG_TOUCHABLE_WHEN_WAKING: { + return "TOUCHABLE_WHEN_WAKING"; + } + case InputWindowInfo::FLAG_KEEP_SCREEN_ON: { + return "KEEP_SCREEN_ON"; + } + case InputWindowInfo::FLAG_LAYOUT_IN_SCREEN: { + return "LAYOUT_IN_SCREEN"; + } + case InputWindowInfo::FLAG_LAYOUT_NO_LIMITS: { + return "LAYOUT_NO_LIMITS"; + } + case InputWindowInfo::FLAG_FULLSCREEN: { + return "FULLSCREEN"; + } + case InputWindowInfo::FLAG_FORCE_NOT_FULLSCREEN: { + return "FORCE_NOT_FULLSCREEN"; + } + case InputWindowInfo::FLAG_DITHER: { + return "DITHER"; + } + case InputWindowInfo::FLAG_SECURE: { + return "SECURE"; + } + case InputWindowInfo::FLAG_SCALED: { + return "SCALED"; + } + case InputWindowInfo::FLAG_IGNORE_CHEEK_PRESSES: { + return "IGNORE_CHEEK_PRESSES"; + } + case InputWindowInfo::FLAG_LAYOUT_INSET_DECOR: { + return "LAYOUT_INSET_DECOR"; + } + case InputWindowInfo::FLAG_ALT_FOCUSABLE_IM: { + return "ALT_FOCUSABLE_IM"; + } + case InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH: { + return "WATCH_OUTSIDE_TOUCH"; + } + case InputWindowInfo::FLAG_SHOW_WHEN_LOCKED: { + return "SHOW_WHEN_LOCKED"; + } + case InputWindowInfo::FLAG_SHOW_WALLPAPER: { + return "SHOW_WALLPAPER"; + } + case InputWindowInfo::FLAG_TURN_SCREEN_ON: { + return "TURN_SCREEN_ON"; + } + case InputWindowInfo::FLAG_DISMISS_KEYGUARD: { + return "DISMISS_KEYGUARD"; + } + case InputWindowInfo::FLAG_SPLIT_TOUCH: { + return "SPLIT_TOUCH"; + } + case InputWindowInfo::FLAG_HARDWARE_ACCELERATED: { + return "HARDWARE_ACCELERATED"; + } + case InputWindowInfo::FLAG_LAYOUT_IN_OVERSCAN: { + return "LAYOUT_IN_OVERSCAN"; + } + case InputWindowInfo::FLAG_TRANSLUCENT_STATUS: { + return "TRANSLUCENT_STATUS"; + } + case InputWindowInfo::FLAG_TRANSLUCENT_NAVIGATION: { + return "TRANSLUCENT_NAVIGATION"; + } + case InputWindowInfo::FLAG_LOCAL_FOCUS_MODE: { + return "LOCAL_FOCUS_MODE"; + } + case InputWindowInfo::FLAG_SLIPPERY: { + return "SLIPPERY"; + } + case InputWindowInfo::FLAG_LAYOUT_ATTACHED_IN_DECOR: { + return "LAYOUT_ATTACHED_IN_DECOR"; + } + case InputWindowInfo::FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS: { + return "DRAWS_SYSTEM_BAR_BACKGROUNDS"; + } + } + return "UNKNOWN"; +} + +std::string inputWindowFlagsToString(uint32_t flags) { + std::string result; + for (BitSet32 bits(flags); !bits.isEmpty();) { + uint32_t bit = bits.clearLastMarkedBit(); // counts from left + const uint32_t flag = 1 << (32 - bit - 1); + result += android::base::StringPrintf("%s | ", inputWindowFlagToString(flag)); + } + return result; +} + // --- InputWindowInfo --- void InputWindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); @@ -42,20 +155,6 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { && y >= frameTop && y < frameBottom; } -// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready. -bool InputWindowInfo::isTrustedOverlay() const { - return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || - layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR || - layoutParamsType == TYPE_NOTIFICATION_SHADE || - layoutParamsType == TYPE_NAVIGATION_BAR || - layoutParamsType == TYPE_NAVIGATION_BAR_PANEL || - layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || - layoutParamsType == TYPE_DOCK_DIVIDER || - layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY || - layoutParamsType == TYPE_INPUT_CONSUMER || - layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY; -} - bool InputWindowInfo::supportsSplitTouch() const { return layoutParamsFlags & FLAG_SPLIT_TOUCH; } @@ -92,6 +191,7 @@ status_t InputWindowInfo::write(Parcel& output) const { output.writeBool(hasFocus); output.writeBool(hasWallpaper); output.writeBool(paused); + output.writeBool(trustedOverlay); output.writeInt32(ownerPid); output.writeInt32(ownerUid); output.writeInt32(inputFeatures); @@ -130,6 +230,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { ret.hasFocus = from.readBool(); ret.hasWallpaper = from.readBool(); ret.paused = from.readBool(); + ret.trustedOverlay = from.readBool(); ret.ownerPid = from.readInt32(); ret.ownerUid = from.readInt32(); ret.inputFeatures = from.readInt32(); diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index c6cc4fc374..7c28ac5c5d 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -104,107 +104,73 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r // --- VelocityTracker --- -// The default velocity tracker strategy. -// Although other strategies are available for testing and comparison purposes, -// this is the strategy that applications will actually use. Be very careful -// when adjusting the default strategy because it can dramatically affect -// (often in a bad way) the user experience. -const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2"; - -VelocityTracker::VelocityTracker(const char* strategy) : - mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { - char value[PROPERTY_VALUE_MAX]; - - // Allow the default strategy to be overridden using a system property for debugging. - if (!strategy) { - int length = property_get("persist.input.velocitytracker.strategy", value, nullptr); - if (length > 0) { - strategy = value; - } else { - strategy = DEFAULT_STRATEGY; - } - } - +VelocityTracker::VelocityTracker(const Strategy strategy) + : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { // Configure the strategy. if (!configureStrategy(strategy)) { - ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy); - if (!configureStrategy(DEFAULT_STRATEGY)) { - LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!", - strategy); + ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy); + if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) { + LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32 + "'!", + strategy); } } } VelocityTracker::~VelocityTracker() { - delete mStrategy; } -bool VelocityTracker::configureStrategy(const char* strategy) { - mStrategy = createStrategy(strategy); +bool VelocityTracker::configureStrategy(Strategy strategy) { + if (strategy == VelocityTracker::Strategy::DEFAULT) { + mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY); + } else { + mStrategy = createStrategy(strategy); + } return mStrategy != nullptr; } -VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { - if (!strcmp("impulse", strategy)) { - // Physical model of pushing an object. Quality: VERY GOOD. - // Works with duplicate coordinates, unclean finger liftoff. - return new ImpulseVelocityTrackerStrategy(); - } - if (!strcmp("lsq1", strategy)) { - // 1st order least squares. Quality: POOR. - // Frequently underfits the touch data especially when the finger accelerates - // or changes direction. Often underestimates velocity. The direction - // is overly influenced by historical touch points. - return new LeastSquaresVelocityTrackerStrategy(1); - } - if (!strcmp("lsq2", strategy)) { - // 2nd order least squares. Quality: VERY GOOD. - // Pretty much ideal, but can be confused by certain kinds of touch data, - // particularly if the panel has a tendency to generate delayed, - // duplicate or jittery touch coordinates when the finger is released. - return new LeastSquaresVelocityTrackerStrategy(2); - } - if (!strcmp("lsq3", strategy)) { - // 3rd order least squares. Quality: UNUSABLE. - // Frequently overfits the touch data yielding wildly divergent estimates - // of the velocity when the finger is released. - return new LeastSquaresVelocityTrackerStrategy(3); - } - if (!strcmp("wlsq2-delta", strategy)) { - // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA); - } - if (!strcmp("wlsq2-central", strategy)) { - // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL); - } - if (!strcmp("wlsq2-recent", strategy)) { - // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT); - } - if (!strcmp("int1", strategy)) { - // 1st order integrating filter. Quality: GOOD. - // Not as good as 'lsq2' because it cannot estimate acceleration but it is - // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate - // the velocity of a fling but this strategy tends to respond to changes in - // direction more quickly and accurately. - return new IntegratingVelocityTrackerStrategy(1); - } - if (!strcmp("int2", strategy)) { - // 2nd order integrating filter. Quality: EXPERIMENTAL. - // For comparison purposes only. Unlike 'int1' this strategy can compensate - // for acceleration but it typically overestimates the effect. - return new IntegratingVelocityTrackerStrategy(2); - } - if (!strcmp("legacy", strategy)) { - // Legacy velocity tracker algorithm. Quality: POOR. - // For comparison purposes only. This algorithm is strongly influenced by - // old data points, consistently underestimates velocity and takes a very long - // time to adjust to changes in direction. - return new LegacyVelocityTrackerStrategy(); +std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( + VelocityTracker::Strategy strategy) { + switch (strategy) { + case VelocityTracker::Strategy::IMPULSE: + return std::make_unique<ImpulseVelocityTrackerStrategy>(); + + case VelocityTracker::Strategy::LSQ1: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1); + + case VelocityTracker::Strategy::LSQ2: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2); + + case VelocityTracker::Strategy::LSQ3: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3); + + case VelocityTracker::Strategy::WLSQ2_DELTA: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_DELTA); + case VelocityTracker::Strategy::WLSQ2_CENTRAL: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_CENTRAL); + case VelocityTracker::Strategy::WLSQ2_RECENT: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_RECENT); + + case VelocityTracker::Strategy::INT1: + return std::make_unique<IntegratingVelocityTrackerStrategy>(1); + + case VelocityTracker::Strategy::INT2: + return std::make_unique<IntegratingVelocityTrackerStrategy>(2); + + case VelocityTracker::Strategy::LEGACY: + return std::make_unique<LegacyVelocityTrackerStrategy>(); + + default: + break; } return nullptr; } diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index bf452c07a3..249d9d4490 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -191,8 +191,9 @@ static std::vector<MotionEvent> createMotionEventStream( return events; } -static void computeAndCheckVelocity(const char* strategy, - const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) { +static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, + const std::vector<MotionEventEntry>& motions, int32_t axis, + float targetVelocity) { VelocityTracker vt(strategy); float Vx, Vy; @@ -217,7 +218,7 @@ static void computeAndCheckVelocity(const char* strategy, static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions, const std::array<float, 3>& coefficients) { - VelocityTracker vt("lsq2"); + VelocityTracker vt(VelocityTracker::Strategy::LSQ2); std::vector<MotionEvent> events = createMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); @@ -243,7 +244,8 @@ TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) { {14730us, {{293, NAN}}}, {14730us, {{293, NAN}}}, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 1600); } TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { @@ -254,8 +256,8 @@ TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { { 11283us, {{293, NAN}} }, { 11283us, {{293, NAN}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0); } TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { @@ -266,8 +268,8 @@ TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { { 20ms, {{10, NAN}} }, { 20ms, {{10, NAN}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500); } @@ -297,8 +299,10 @@ TEST_F(VelocityTrackerTest, SwordfishFlingDown) { { 96948871ns, {{274.79245, 428.113159}} }, { 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 623.577637); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 5970.7309); } // --------------- Recorded by hand on sailfish, generated by a script ----------------------------- @@ -339,10 +343,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { { 235089162955851ns, {{560.66, 843.82}} }, { 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181); - computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 872.794617); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 951.698181); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -3604.819336); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3044.966064); } @@ -368,8 +376,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { { 235110660368000ns, {{530.00, 980.00}} }, { 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -4096.583008); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3455.094238); } @@ -396,10 +406,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { { 792629200000ns, {{619.00, 1115.00}} }, { 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 574.33429); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 617.40564); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -2361.982666); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -2500.055664); } @@ -426,10 +440,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { { 235160520366000ns, {{679.00, 814.00}} }, { 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 1274.141724); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 1438.53186); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -3001.4348); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3695.859619); } @@ -452,8 +470,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { { 847237986000ns, {{610.00, 1095.00}} }, { 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -4280.07959); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -4241.004395); } @@ -476,8 +496,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { { 235200616933000ns, {{590.00, 844.00}} }, { 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -8715.686523); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -7639.026367); } @@ -499,10 +521,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { { 920989261000ns, {{715.00, 903.00}} }, { 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 5670.329102); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 5991.866699); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -13021.101562); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -15093.995117); } @@ -522,8 +548,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { { 235247220736000ns, {{620.00, 641.00}} }, { 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -20286.958984); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -20494.587891); } @@ -541,8 +569,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { { 235302613019881ns, {{679.26, 526.73}} }, { 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -39295.941406); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -36461.421875); } @@ -569,10 +599,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { { 235655842893000ns, {{563.00, 649.00}} }, { 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -419.749695); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -398.303894); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 3309.016357); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 3969.099854); } @@ -599,10 +633,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { { 235671246532000ns, {{470.00, 799.00}} }, { 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -262.80426); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -243.665344); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4215.682129); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4587.986816); } @@ -622,10 +660,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { { 171051052000ns, {{536.00, 586.00}} }, { 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -723.413513); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -651.038452); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 2091.502441); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 1934.517456); } @@ -652,8 +694,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { { 235695373403000ns, {{564.00, 744.00}} }, { 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4254.639648); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4698.415039); } @@ -677,10 +721,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { { 235709710626776ns, {{511.72, 741.85}} }, { 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -430.440247); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -447.600311); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 3953.859375); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4316.155273); } @@ -706,8 +754,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { { 235727721580000ns, {{516.00, 658.00}} }, { 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4484.617676); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4927.92627); } @@ -725,8 +775,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { { 235762396429369ns, {{404.37, 680.67}} }, { 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 14227.0224); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 16064.685547); } @@ -744,8 +796,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { { 235772537635000ns, {{484.00, 589.00}} }, { 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 18660.048828); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 16918.439453); } @@ -764,10 +818,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { { 507703352649ns, {{443.71, 857.77}} }, { 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -4111.8173); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -6388.48877); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 29765.908203); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 28354.796875); } /** @@ -789,10 +847,10 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFi // Velocity should actually be zero, but we expect 0.016 here instead. // This is close enough to zero, and is likely caused by division by a very small number. - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0); } /** diff --git a/libs/math/Android.bp b/libs/math/Android.bp index 3b1edcb43a..22d471204a 100644 --- a/libs/math/Android.bp +++ b/libs/math/Android.bp @@ -25,6 +25,11 @@ cc_library_static { min_sdk_version: "29", export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + } + } } subdirs = ["tests"] diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h index 76829734a4..617a0ab5d2 100644 --- a/libs/math/include/math/half.h +++ b/libs/math/include/math/half.h @@ -82,6 +82,7 @@ class half { }; public: + CONSTEXPR half() noexcept { } CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { } CONSTEXPR operator float() const noexcept { return htof(mBits); } diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp index 496a7ef56d..604072e557 100644 --- a/libs/math/tests/half_test.cpp +++ b/libs/math/tests/half_test.cpp @@ -35,6 +35,7 @@ TEST_F(HalfTest, Basics) { EXPECT_EQ(2UL, sizeof(half)); // test +/- zero + EXPECT_EQ(0x0000, half().getBits()); EXPECT_EQ(0x0000, half( 0.0f).getBits()); EXPECT_EQ(0x8000, half(-0.0f).getBits()); diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index eb6080fc21..3dcb49895d 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -64,6 +64,13 @@ filegroup { ], } +filegroup { + name: "librenderengine_threaded_sources", + srcs: [ + "threaded/RenderEngineThreaded.cpp", + ], +} + cc_library_static { name: "librenderengine", defaults: ["librenderengine_defaults"], @@ -80,6 +87,7 @@ cc_library_static { srcs: [ ":librenderengine_sources", ":librenderengine_gl_sources", + ":librenderengine_threaded_sources", ], lto: { thin: true, diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 0fdf093b2f..c3fbb60af2 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -20,19 +20,34 @@ #include <log/log.h> #include <private/gui/SyncFeatures.h> #include "gl/GLESRenderEngine.h" +#include "threaded/RenderEngineThreaded.h" namespace android { namespace renderengine { -std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { +std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { + RenderEngineType renderEngineType = args.renderEngineType; + + // Keep the ability to override by PROPERTIES: char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles"); + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); if (strcmp(prop, "gles") == 0) { - ALOGD("RenderEngine GLES Backend"); - return renderengine::gl::GLESRenderEngine::create(args); + renderEngineType = RenderEngineType::GLES; + } + if (strcmp(prop, "threaded") == 0) { + renderEngineType = RenderEngineType::THREADED; + } + + switch (renderEngineType) { + case RenderEngineType::THREADED: + ALOGD("Threaded RenderEngine with GLES Backend"); + return renderengine::threaded::RenderEngineThreaded::create( + [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); }); + case RenderEngineType::GLES: + default: + ALOGD("RenderEngine with GLES Backend"); + return renderengine::gl::GLESRenderEngine::create(args); } - ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); - return renderengine::gl::GLESRenderEngine::create(args); } RenderEngine::~RenderEngine() = default; diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 92e7e715ea..d102696382 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -982,7 +982,7 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* const buffer, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) { ATRACE_CALL(); @@ -1019,7 +1019,9 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const auto blurLayersSize = blurLayers.size(); if (blurLayersSize == 0) { - fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache); + fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, + buffer.get()->getNativeBuffer(), + useFramebufferCache); if (fbo->getStatus() != NO_ERROR) { ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", buffer->handle); @@ -1076,7 +1078,9 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, if (blurLayers.size() == 0) { // Done blurring, time to bind the native FBO and render our blur onto it. - fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, + fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, + buffer.get() + ->getNativeBuffer(), useFramebufferCache); status = fbo->getStatus(); setViewportAndProjection(display.physicalDisplay, display.clip); diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 42b8537b94..9ab5ee6dba 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -73,7 +73,7 @@ public: bool useProtectedContext(bool useProtectedContext) override; status_t drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* buffer, const bool useFramebufferCache, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; bool cleanupPostRender() override; diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index e06e1287c1..b137023b84 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -50,6 +50,10 @@ class Mesh; class Texture; struct RenderEngineCreationArgs; +namespace threaded { +class RenderEngineThreaded; +} + namespace impl { class RenderEngine; } @@ -67,7 +71,12 @@ public: HIGH = 3, }; - static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args); + enum class RenderEngineType { + GLES = 1, + THREADED = 2, + }; + + static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); virtual ~RenderEngine() = 0; @@ -163,7 +172,7 @@ public: // now, this always returns NO_ERROR. virtual status_t drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* buffer, const bool useFramebufferCache, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0; protected: @@ -174,6 +183,7 @@ protected: // live longer than RenderEngine. virtual Framebuffer* getFramebufferForDrawing() = 0; friend class BindNativeBufferAsFramebuffer; + friend class threaded::RenderEngineThreaded; }; struct RenderEngineCreationArgs { @@ -184,26 +194,25 @@ struct RenderEngineCreationArgs { bool precacheToneMapperShaderOnly; bool supportsBackgroundBlur; RenderEngine::ContextPriority contextPriority; + RenderEngine::RenderEngineType renderEngineType; struct Builder; private: // must be created by Builder via constructor with full argument list - RenderEngineCreationArgs( - int _pixelFormat, - uint32_t _imageCacheSize, - bool _useColorManagement, - bool _enableProtectedContext, - bool _precacheToneMapperShaderOnly, - bool _supportsBackgroundBlur, - RenderEngine::ContextPriority _contextPriority) - : pixelFormat(_pixelFormat) - , imageCacheSize(_imageCacheSize) - , useColorManagement(_useColorManagement) - , enableProtectedContext(_enableProtectedContext) - , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly) - , supportsBackgroundBlur(_supportsBackgroundBlur) - , contextPriority(_contextPriority) {} + RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement, + bool _enableProtectedContext, bool _precacheToneMapperShaderOnly, + bool _supportsBackgroundBlur, + RenderEngine::ContextPriority _contextPriority, + RenderEngine::RenderEngineType _renderEngineType) + : pixelFormat(_pixelFormat), + imageCacheSize(_imageCacheSize), + useColorManagement(_useColorManagement), + enableProtectedContext(_enableProtectedContext), + precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly), + supportsBackgroundBlur(_supportsBackgroundBlur), + contextPriority(_contextPriority), + renderEngineType(_renderEngineType) {} RenderEngineCreationArgs() = delete; }; @@ -238,10 +247,14 @@ struct RenderEngineCreationArgs::Builder { this->contextPriority = contextPriority; return *this; } + Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) { + this->renderEngineType = renderEngineType; + return *this; + } RenderEngineCreationArgs build() const { return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement, enableProtectedContext, precacheToneMapperShaderOnly, - supportsBackgroundBlur, contextPriority); + supportsBackgroundBlur, contextPriority, renderEngineType); } private: @@ -253,6 +266,7 @@ private: bool precacheToneMapperShaderOnly = false; bool supportsBackgroundBlur = false; RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM; + RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES; }; class BindNativeBufferAsFramebuffer { diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index df0f17a6d5..d0343ba5a4 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -59,7 +59,8 @@ public: MOCK_METHOD0(cleanupPostRender, bool()); MOCK_METHOD6(drawLayers, status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&, - ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*)); + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, + base::unique_fd*)); }; } // namespace mock diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index e98babc30c..bcf389b2c2 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -18,10 +18,12 @@ cc_test { test_suites: ["device-tests"], srcs: [ "RenderEngineTest.cpp", + "RenderEngineThreadedTest.cpp", ], static_libs: [ "libgmock", "librenderengine", + "librenderengine_mocks", ], shared_libs: [ "libbase", diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 16a8a0decb..77b6c0f4f5 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -22,12 +22,13 @@ #include <condition_variable> #include <fstream> -#include <gtest/gtest.h> #include <cutils/properties.h> +#include <gtest/gtest.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <ui/PixelFormat.h> #include "../gl/GLESRenderEngine.h" +#include "../threaded/RenderEngineThreaded.h" constexpr int DEFAULT_DISPLAY_WIDTH = 128; constexpr int DEFAULT_DISPLAY_HEIGHT = 256; @@ -40,14 +41,15 @@ struct RenderEngineTest : public ::testing::Test { static void SetUpTestSuite() { sRE = renderengine::gl::GLESRenderEngine::create( renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setUseColorManagerment(false) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .build()); + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(1) + .setUseColorManagerment(false) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) + .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) + .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES) + .build()); } static void TearDownTestSuite() { @@ -252,8 +254,8 @@ struct RenderEngineTest : public ::testing::Test { std::vector<const renderengine::LayerSettings*> layers, sp<GraphicBuffer> buffer) { base::unique_fd fence; - status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true, - base::unique_fd(), &fence); + status_t status = + sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence); sCurrentBuffer = buffer; int fd = fence.release(); @@ -1004,8 +1006,7 @@ TEST_F(RenderEngineTest, drawLayers_nullOutputFence) { layer.alpha = 1.0; layers.push_back(&layer); - status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, - base::unique_fd(), nullptr); + status_t status = sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr); sCurrentBuffer = mBuffer; ASSERT_EQ(NO_ERROR, status); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); @@ -1023,8 +1024,7 @@ TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { layer.alpha = 1.0; layers.push_back(&layer); - status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false, - base::unique_fd(), nullptr); + status_t status = sRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr); sCurrentBuffer = mBuffer; ASSERT_EQ(NO_ERROR, status); ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId())); @@ -1414,11 +1414,9 @@ TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) { layers.push_back(&layer); base::unique_fd fenceOne; - sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), - &fenceOne); + sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne); base::unique_fd fenceTwo; - sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne), - &fenceTwo); + sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo); const int fd = fenceTwo.get(); if (fd >= 0) { diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp new file mode 100644 index 0000000000..69a0e1951a --- /dev/null +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -0,0 +1,210 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cutils/properties.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <renderengine/mock/RenderEngine.h> +#include "../threaded/RenderEngineThreaded.h" + +namespace android { + +using testing::_; +using testing::Eq; +using testing::Mock; +using testing::Return; + +struct RenderEngineThreadedTest : public ::testing::Test { + ~RenderEngineThreadedTest() {} + + void SetUp() override { + mThreadedRE = renderengine::threaded::RenderEngineThreaded::create( + [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }); + } + + std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); +}; + +TEST_F(RenderEngineThreadedTest, dump) { + std::string testString = "XYZ"; + EXPECT_CALL(*mRenderEngine, dump(_)); + mThreadedRE->dump(testString); +} + +TEST_F(RenderEngineThreadedTest, primeCache) { + EXPECT_CALL(*mRenderEngine, primeCache()); + mThreadedRE->primeCache(); +} + +TEST_F(RenderEngineThreadedTest, genTextures) { + uint32_t texName; + EXPECT_CALL(*mRenderEngine, genTextures(1, &texName)); + mThreadedRE->genTextures(1, &texName); +} + +TEST_F(RenderEngineThreadedTest, deleteTextures) { + uint32_t texName; + EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName)); + mThreadedRE->deleteTextures(1, &texName); +} + +TEST_F(RenderEngineThreadedTest, bindExternalBuffer_nullptrBuffer) { + EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, Eq(nullptr), Eq(nullptr))) + .WillOnce(Return(BAD_VALUE)); + status_t result = mThreadedRE->bindExternalTextureBuffer(0, nullptr, nullptr); + ASSERT_EQ(BAD_VALUE, result); +} + +TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) { + sp<GraphicBuffer> buf = new GraphicBuffer(); + EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr))) + .WillOnce(Return(NO_ERROR)); + status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr); + ASSERT_EQ(NO_ERROR, result); +} + +TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) { + EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr))); + mThreadedRE->cacheExternalTextureBuffer(nullptr); +} + +TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) { + sp<GraphicBuffer> buf = new GraphicBuffer(); + EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf)); + mThreadedRE->cacheExternalTextureBuffer(buf); +} + +TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) { + EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0)); + mThreadedRE->unbindExternalTextureBuffer(0x0); +} + +TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsBadValue) { + std::unique_ptr<renderengine::Framebuffer> framebuffer; + EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(BAD_VALUE)); + status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get()); + ASSERT_EQ(BAD_VALUE, result); +} + +TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsNoError) { + std::unique_ptr<renderengine::Framebuffer> framebuffer; + EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(NO_ERROR)); + status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get()); + ASSERT_EQ(NO_ERROR, result); +} + +TEST_F(RenderEngineThreadedTest, unbindFrameBuffer) { + std::unique_ptr<renderengine::Framebuffer> framebuffer; + EXPECT_CALL(*mRenderEngine, unbindFrameBuffer(framebuffer.get())); + mThreadedRE->unbindFrameBuffer(framebuffer.get()); +} + +TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) { + size_t size = 20; + EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size)); + size_t result = mThreadedRE->getMaxTextureSize(); + ASSERT_EQ(size, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) { + size_t size = 0; + EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size)); + size_t result = mThreadedRE->getMaxTextureSize(); + ASSERT_EQ(size, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) { + size_t dims = 20; + EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims)); + size_t result = mThreadedRE->getMaxViewportDims(); + ASSERT_EQ(dims, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) { + size_t dims = 0; + EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims)); + size_t result = mThreadedRE->getMaxViewportDims(); + ASSERT_EQ(dims, result); +} + +TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) { + EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); + status_t result = mThreadedRE->isProtected(); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) { + EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true)); + size_t result = mThreadedRE->isProtected(); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) { + EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false)); + status_t result = mThreadedRE->supportsProtectedContent(); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) { + EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true)); + status_t result = mThreadedRE->supportsProtectedContent(); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) { + EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false)); + status_t result = mThreadedRE->useProtectedContext(false); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) { + EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true)); + status_t result = mThreadedRE->useProtectedContext(false); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) { + EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(false)); + status_t result = mThreadedRE->cleanupPostRender(); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) { + EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(true)); + status_t result = mThreadedRE->cleanupPostRender(); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, drawLayers) { + renderengine::DisplaySettings settings; + std::vector<const renderengine::LayerSettings*> layers; + sp<GraphicBuffer> buffer = new GraphicBuffer(); + base::unique_fd bufferFence; + base::unique_fd drawFence; + + EXPECT_CALL(*mRenderEngine, drawLayers) + .WillOnce([](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, + base::unique_fd*) -> status_t { return NO_ERROR; }); + + status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false, + std::move(bufferFence), &drawFence); + ASSERT_EQ(NO_ERROR, result); +} + +} // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp new file mode 100644 index 0000000000..ad6171874d --- /dev/null +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -0,0 +1,403 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "RenderEngineThreaded.h" + +#include <sched.h> +#include <chrono> +#include <future> + +#include <android-base/stringprintf.h> +#include <private/gui/SyncFeatures.h> +#include <utils/Trace.h> + +#include "gl/GLESRenderEngine.h" + +using namespace std::chrono_literals; + +namespace android { +namespace renderengine { +namespace threaded { + +std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) { + return std::make_unique<RenderEngineThreaded>(std::move(factory)); +} + +RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) { + ATRACE_CALL(); + + std::lock_guard lockThread(mThreadMutex); + mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory); +} + +RenderEngineThreaded::~RenderEngineThreaded() { + { + std::lock_guard lock(mThreadMutex); + mRunning = false; + mCondition.notify_one(); + } + + if (mThread.joinable()) { + mThread.join(); + } +} + +// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations. +void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS { + ATRACE_CALL(); + + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO"); + } + + mRenderEngine = factory(); + + std::unique_lock<std::mutex> lock(mThreadMutex); + pthread_setname_np(pthread_self(), mThreadName); + + while (mRunning) { + if (!mFunctionCalls.empty()) { + auto task = mFunctionCalls.front(); + mFunctionCalls.pop(); + task(*mRenderEngine); + } + mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) { + return !mRunning || !mFunctionCalls.empty(); + }); + } +} + +void RenderEngineThreaded::primeCache() const { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::primeCache"); + instance.primeCache(); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::dump(std::string& result) { + std::promise<std::string> resultPromise; + std::future<std::string> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::dump"); + std::string localResult = result; + instance.dump(localResult); + resultPromise.set_value(std::move(localResult)); + }); + } + mCondition.notify_one(); + // Note: This is an rvalue. + result.assign(resultFuture.get()); +} + +bool RenderEngineThreaded::useNativeFenceSync() const { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) { + ATRACE_NAME("REThreaded::useNativeFenceSync"); + bool returnValue = SyncFeatures::getInstance().useNativeFenceSync(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::useWaitSync() const { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) { + ATRACE_NAME("REThreaded::useWaitSync"); + bool returnValue = SyncFeatures::getInstance().useWaitSync(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::genTextures"); + instance.genTextures(count, names); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::deleteTextures"); + instance.deleteTextures(count, names); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::bindExternalTextureImage(uint32_t texName, const Image& image) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push( + [&resultPromise, texName, &image](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::bindExternalTextureImage"); + instance.bindExternalTextureImage(texName, image); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +status_t RenderEngineThreaded::bindExternalTextureBuffer(uint32_t texName, + const sp<GraphicBuffer>& buffer, + const sp<Fence>& fence) { + std::promise<status_t> resultPromise; + std::future<status_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push( + [&resultPromise, texName, &buffer, &fence](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::bindExternalTextureBuffer"); + status_t status = instance.bindExternalTextureBuffer(texName, buffer, fence); + resultPromise.set_value(status); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::cacheExternalTextureBuffer"); + instance.cacheExternalTextureBuffer(buffer); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::unbindExternalTextureBuffer"); + instance.unbindExternalTextureBuffer(bufferId); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +status_t RenderEngineThreaded::bindFrameBuffer(Framebuffer* framebuffer) { + std::promise<status_t> resultPromise; + std::future<status_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::bindFrameBuffer"); + status_t status = instance.bindFrameBuffer(framebuffer); + resultPromise.set_value(status); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +void RenderEngineThreaded::unbindFrameBuffer(Framebuffer* framebuffer) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::unbindFrameBuffer"); + instance.unbindFrameBuffer(framebuffer); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +size_t RenderEngineThreaded::getMaxTextureSize() const { + std::promise<size_t> resultPromise; + std::future<size_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::getMaxTextureSize"); + size_t size = instance.getMaxTextureSize(); + resultPromise.set_value(size); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +size_t RenderEngineThreaded::getMaxViewportDims() const { + std::promise<size_t> resultPromise; + std::future<size_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::getMaxViewportDims"); + size_t size = instance.getMaxViewportDims(); + resultPromise.set_value(size); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::isProtected() const { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::isProtected"); + bool returnValue = instance.isProtected(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::supportsProtectedContent() const { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::supportsProtectedContent"); + bool returnValue = instance.supportsProtectedContent(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push( + [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::useProtectedContext"); + bool returnValue = instance.useProtectedContext(useProtectedContext); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +Framebuffer* RenderEngineThreaded::getFramebufferForDrawing() { + std::promise<Framebuffer*> resultPromise; + std::future<Framebuffer*> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::getFramebufferForDrawing"); + Framebuffer* framebuffer = instance.getFramebufferForDrawing(); + resultPromise.set_value(framebuffer); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::cleanupPostRender() { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::cleanupPostRender"); + bool returnValue = instance.cleanupPostRender(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence, + base::unique_fd* drawFence) { + std::promise<status_t> resultPromise; + std::future<status_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache, + &bufferFence, &drawFence](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::drawLayers"); + status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache, + std::move(bufferFence), drawFence); + resultPromise.set_value(status); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +} // namespace threaded +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h new file mode 100644 index 0000000000..ec18e1f7b5 --- /dev/null +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -0,0 +1,97 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/thread_annotations.h> +#include <condition_variable> +#include <mutex> +#include <queue> +#include <thread> + +#include "renderengine/RenderEngine.h" + +namespace android { +namespace renderengine { +namespace threaded { + +using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>; + +/** + * This class extends a basic RenderEngine class. It contains a thread. Each time a function of + * this class is called, we create a lambda function that is put on a queue. The main thread then + * executes the functions in order. + */ +class RenderEngineThreaded : public RenderEngine { +public: + static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory); + + RenderEngineThreaded(CreateInstanceFactory factory); + ~RenderEngineThreaded() override; + void primeCache() const override; + + void dump(std::string& result) override; + + bool useNativeFenceSync() const override; + bool useWaitSync() const override; + void genTextures(size_t count, uint32_t* names) override; + void deleteTextures(size_t count, uint32_t const* names) override; + void bindExternalTextureImage(uint32_t texName, const Image& image) override; + status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, + const sp<Fence>& fence) override; + void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; + void unbindExternalTextureBuffer(uint64_t bufferId) override; + status_t bindFrameBuffer(Framebuffer* framebuffer) override; + void unbindFrameBuffer(Framebuffer* framebuffer) override; + size_t getMaxTextureSize() const override; + size_t getMaxViewportDims() const override; + + bool isProtected() const override; + bool supportsProtectedContent() const override; + bool useProtectedContext(bool useProtectedContext) override; + bool cleanupPostRender() override; + + status_t drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; + +protected: + Framebuffer* getFramebufferForDrawing() override; + +private: + void threadMain(CreateInstanceFactory factory); + + /* ------------------------------------------------------------------------ + * Threading + */ + const char* const mThreadName = "RenderEngineThread"; + // Protects the creation and destruction of mThread. + mutable std::mutex mThreadMutex; + std::thread mThread GUARDED_BY(mThreadMutex); + bool mRunning GUARDED_BY(mThreadMutex) = true; + mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls + GUARDED_BY(mThreadMutex); + mutable std::condition_variable mCondition; + + /* ------------------------------------------------------------------------ + * Render Engine + */ + std::unique_ptr<renderengine::RenderEngine> mRenderEngine; +}; +} // namespace threaded +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index 8ed09f8ff0..a6b0aaf0b5 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -216,14 +216,25 @@ status_t BnSensorServer::onTransact( int32_t type; Vector<float> floats; Vector<int32_t> ints; + uint32_t count; handle = data.readInt32(); type = data.readInt32(); - floats.resize(data.readUint32()); + + count = data.readUint32(); + if (count > (data.dataAvail() / sizeof(float))) { + return BAD_VALUE; + } + floats.resize(count); for (auto &i : floats) { i = data.readFloat(); } - ints.resize(data.readUint32()); + + count = data.readUint32(); + if (count > (data.dataAvail() / sizeof(int32_t))) { + return BAD_VALUE; + } + ints.resize(count); for (auto &i : ints) { i = data.readInt32(); } diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 1ee8c7105c..f3edd3c791 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -12,6 +12,66 @@ // See the License for the specific language governing permissions and // limitations under the License. +cc_defaults { + name: "libui-defaults", + clang: true, + cflags: [ + "-Wall", + "-Werror", + ], + cppflags: [ + "-Wextra", + ], + + sanitize: { + integer_overflow: true, + misc_undefined: ["bounds"], + }, + +} + +cc_library_static { + name: "libui-types", + vendor_available: true, + host_supported: true, + target: { + windows: { + enabled: true, + } + }, + + defaults: [ + "libui-defaults", + ], + + apex_available: [ + "//apex_available:anyapex", + "//apex_available:platform", + ], + min_sdk_version: "apex_inherit", + + shared_libs: [ + "libutils", + ], + + static_libs: [ + "libmath", + ], + + srcs: [ + "ColorSpace.cpp", + ], + + export_include_dirs: [ + "include_types", + ], + + export_static_lib_headers: [ + "libmath", + ], + +} + cc_library_shared { name: "libui", vendor_available: true, @@ -35,8 +95,9 @@ cc_library_shared { }, srcs: [ - "ColorSpace.cpp", "DebugUtils.cpp", + "DeviceProductInfo.cpp", + "DisplayInfo.cpp", "Fence.cpp", "FenceTime.cpp", "FrameStats.cpp", @@ -65,8 +126,11 @@ cc_library_shared { "include_private", ], - // Uncomment the following line to enable VALIDATE_REGIONS traces - //defaults: ["libui-validate-regions-defaults"], + defaults: [ + "libui-defaults", + // Uncomment the following line to enable VALIDATE_REGIONS traces + //defaults: ["libui-validate-regions-defaults"], + ], shared_libs: [ "android.hardware.graphics.allocator@2.0", @@ -100,6 +164,10 @@ cc_library_shared { "libmath", ], + whole_static_libs: [ + "libui-types", + ], + // bufferhub is not used when building libgui for vendors target: { vendor: { diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp new file mode 100644 index 0000000000..4d6ce4306a --- /dev/null +++ b/libs/ui/DeviceProductInfo.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/DeviceProductInfo.h> + +#include <android-base/stringprintf.h> +#include <ui/FlattenableHelpers.h> +#include <utils/Log.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android { + +using base::StringAppendF; + +size_t DeviceProductInfo::getFlattenedSize() const { + return FlattenableHelpers::getFlattenedSize(name) + + FlattenableHelpers::getFlattenedSize(manufacturerPnpId) + + FlattenableHelpers::getFlattenedSize(productId) + + FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) + + FlattenableHelpers::getFlattenedSize(relativeAddress); +} + +status_t DeviceProductInfo::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress)); + return OK; +} + +status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) { + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress)); + return OK; +} + +void DeviceProductInfo::dump(std::string& result) const { + StringAppendF(&result, "{name=%s, ", name.c_str()); + StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data()); + StringAppendF(&result, "productId=%s, ", productId.c_str()); + + if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "modelYear=%u, ", model->year); + } else if (const auto* manufactureWeekAndYear = + std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week); + StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year); + } else if (const auto* manufactureYear = + std::get_if<ManufactureYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year); + } else { + ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate"); + } + + result.append("relativeAddress=["); + for (size_t i = 0; i < relativeAddress.size(); i++) { + if (i != 0) { + result.append(", "); + } + StringAppendF(&result, "%u", relativeAddress[i]); + } + result.append("]}"); +} + +} // namespace android diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/DisplayInfo.cpp new file mode 100644 index 0000000000..73a78af186 --- /dev/null +++ b/libs/ui/DisplayInfo.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/DisplayInfo.h> + +#include <cstdint> + +#include <ui/FlattenableHelpers.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android { + +size_t DisplayInfo::getFlattenedSize() const { + return FlattenableHelpers::getFlattenedSize(connectionType) + + FlattenableHelpers::getFlattenedSize(density) + + FlattenableHelpers::getFlattenedSize(secure) + + FlattenableHelpers::getFlattenedSize(deviceProductInfo); +} + +status_t DisplayInfo::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo)); + return OK; +} + +status_t DisplayInfo::unflatten(void const* buffer, size_t size) { + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo)); + return OK; +} + +} // namespace android diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h index af00342c0c..807a5d96a3 100644 --- a/libs/ui/include/ui/DeviceProductInfo.h +++ b/libs/ui/include/ui/DeviceProductInfo.h @@ -19,7 +19,12 @@ #include <array> #include <cstdint> #include <optional> +#include <string> +#include <type_traits> #include <variant> +#include <vector> + +#include <utils/Flattenable.h> namespace android { @@ -29,13 +34,7 @@ using PnpId = std::array<char, 4>; // Product-specific information about the display or the directly connected device on the // display chain. For example, if the display is transitively connected, this field may contain // product information about the intermediate device. -struct DeviceProductInfo { - static constexpr size_t TEXT_BUFFER_SIZE = 20; - static constexpr size_t RELATIVE_ADDRESS_SIZE = 4; - - using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>; - static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff}; - +struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> { struct ModelYear { uint32_t year; }; @@ -48,21 +47,29 @@ struct DeviceProductInfo { }; // Display name. - std::array<char, TEXT_BUFFER_SIZE> name; + std::string name; // Manufacturer Plug and Play ID. PnpId manufacturerPnpId; // Manufacturer product ID. - std::array<char, TEXT_BUFFER_SIZE> productId; + std::string productId; using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>; + static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>); ManufactureOrModelDate manufactureOrModelDate; - // Relative address in the display network. Unavailable address is indicated - // by all elements equal to 255. + // Relative address in the display network. Empty vector indicates that the + // address is unavailable. // For example, for HDMI connected device this will be the physical address. - RelativeAddress relativeAddress; + std::vector<uint8_t> relativeAddress; + + bool isFixedSize() const { return false; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + void dump(std::string& result) const; }; } // namespace android diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h index 897060c2ed..03e0a3886e 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h @@ -20,19 +20,23 @@ #include <type_traits> #include <ui/DeviceProductInfo.h> +#include <utils/Flattenable.h> namespace android { enum class DisplayConnectionType { Internal, External }; // Immutable information about physical display. -struct DisplayInfo { +struct DisplayInfo : LightFlattenable<DisplayInfo> { DisplayConnectionType connectionType = DisplayConnectionType::Internal; float density = 0.f; bool secure = false; std::optional<DeviceProductInfo> deviceProductInfo; -}; -static_assert(std::is_trivially_copyable_v<DisplayInfo>); + bool isFixedSize() const { return false; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); +}; } // namespace android diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h new file mode 100644 index 0000000000..8e316d8ae0 --- /dev/null +++ b/libs/ui/include_private/ui/FlattenableHelpers.h @@ -0,0 +1,160 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <numeric> +#include <optional> +#include <type_traits> +#include <vector> + +#include <utils/Flattenable.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android { + +struct FlattenableHelpers { + // Helpers for reading and writing POD structures + template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + static constexpr size_t getFlattenedSize(const T&) { + return sizeof(T); + } + + template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + static status_t flatten(void** buffer, size_t* size, const T& value) { + if (*size < sizeof(T)) return NO_MEMORY; + FlattenableUtils::write(*buffer, *size, value); + return OK; + } + + template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + static status_t unflatten(const void** buffer, size_t* size, T* value) { + if (*size < sizeof(T)) return NO_MEMORY; + FlattenableUtils::read(*buffer, *size, *value); + return OK; + } + + // Helpers for reading and writing std::string + static size_t getFlattenedSize(const std::string& str) { + return sizeof(uint64_t) + str.length(); + } + + static status_t flatten(void** buffer, size_t* size, const std::string& str) { + if (*size < getFlattenedSize(str)) return NO_MEMORY; + flatten(buffer, size, (uint64_t)str.length()); + memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length()); + FlattenableUtils::advance(*buffer, *size, str.length()); + return OK; + } + + static status_t unflatten(const void** buffer, size_t* size, std::string* str) { + uint64_t length; + RETURN_IF_ERROR(unflatten(buffer, size, &length)); + if (*size < length) return NO_MEMORY; + str->assign(reinterpret_cast<const char*>(*buffer), length); + FlattenableUtils::advance(*buffer, *size, length); + return OK; + } + + // Helpers for reading and writing LightFlattenable + template <class T> + static size_t getFlattenedSize(const LightFlattenable<T>& value) { + return value.getFlattenedSize(); + } + + template <class T> + static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) { + RETURN_IF_ERROR(value.flatten(*buffer, *size)); + FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize()); + return OK; + } + + template <class T> + static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) { + RETURN_IF_ERROR(value->unflatten(*buffer, *size)); + FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize()); + return OK; + } + + // Helpers for reading and writing std::optional + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static size_t getFlattenedSize(const std::optional<T>& value) { + return sizeof(bool) + (value ? getFlattenedSize(*value) : 0); + } + + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) { + if (value) { + RETURN_IF_ERROR(flatten(buffer, size, true)); + RETURN_IF_ERROR(flatten(buffer, size, *value)); + } else { + RETURN_IF_ERROR(flatten(buffer, size, false)); + } + return OK; + } + + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) { + bool isPresent; + RETURN_IF_ERROR(unflatten(buffer, size, &isPresent)); + if (isPresent) { + *value = T(); + RETURN_IF_ERROR(unflatten(buffer, size, &(**value))); + } else { + value->reset(); + } + return OK; + } + + // Helpers for reading and writing std::vector + template <class T> + static size_t getFlattenedSize(const std::vector<T>& value) { + return std::accumulate(value.begin(), value.end(), sizeof(uint64_t), + [](size_t sum, const T& element) { + return sum + getFlattenedSize(element); + }); + } + + template <class T> + static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) { + RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size())); + for (const auto& element : value) { + RETURN_IF_ERROR(flatten(buffer, size, element)); + } + return OK; + } + + template <class T> + static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) { + uint64_t numElements; + RETURN_IF_ERROR(unflatten(buffer, size, &numElements)); + // We don't need an extra size check since each iteration of the loop does that + std::vector<T> elements; + for (size_t i = 0; i < numElements; i++) { + T element; + RETURN_IF_ERROR(unflatten(buffer, size, &element)); + elements.push_back(element); + } + *value = std::move(elements); + return OK; + } +}; + +} // namespace android + +#undef RETURN_IF_ERROR
\ No newline at end of file diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include_types/ui/ColorSpace.h index 241ec106c0..241ec106c0 100644 --- a/libs/ui/include/ui/ColorSpace.h +++ b/libs/ui/include_types/ui/ColorSpace.h diff --git a/libs/ui/include_vndk/ui/ColorSpace.h b/libs/ui/include_vndk/ui/ColorSpace.h index ddf70d5bdf..7d2a6d307e 120000 --- a/libs/ui/include_vndk/ui/ColorSpace.h +++ b/libs/ui/include_vndk/ui/ColorSpace.h @@ -1 +1 @@ -../../include/ui/ColorSpace.h
\ No newline at end of file +../../include_types/ui/ColorSpace.h
\ No newline at end of file diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index b53342cb79..28ef77a496 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -29,6 +29,13 @@ cc_test { } cc_test { + name: "FlattenableHelpers_test", + shared_libs: ["libui"], + srcs: ["FlattenableHelpers_test.cpp"], + cflags: ["-Wall", "-Werror"], +} + +cc_test { name: "GraphicBufferAllocator_test", header_libs: [ "libnativewindow_headers", diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp new file mode 100644 index 0000000000..db32bc7596 --- /dev/null +++ b/libs/ui/tests/FlattenableHelpers_test.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "FlattenableHelpersTest" + +#include <ui/FlattenableHelpers.h> + +#include <gtest/gtest.h> +#include <utils/Flattenable.h> +#include <cstdint> +#include <memory> +#include <optional> +#include <string> +#include <vector> + +namespace android { + +namespace { + +struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> { + std::unique_ptr<int32_t> ptr; + + bool isFixedSize() const { return true; } + size_t getFlattenedSize() const { return sizeof(int32_t); } + + status_t flatten(void* buffer, size_t size) const { + FlattenableUtils::write(buffer, size, *ptr); + return OK; + } + + status_t unflatten(void const* buffer, size_t size) { + int value; + FlattenableUtils::read(buffer, size, value); + ptr = std::make_unique<int32_t>(value); + return OK; + } +}; + +class FlattenableHelpersTest : public testing::Test { +public: + template <class T> + void testWriteThenRead(const T& value, size_t bufferSize) { + std::vector<int8_t> buffer(bufferSize); + auto rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + + auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size = buffer.size(); + T valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + EXPECT_EQ(value, valueRead); + } + + template <class T> + void testTriviallyCopyable(const T& value) { + testWriteThenRead(value, sizeof(T)); + } + + template <class T> + void testWriteThenRead(const T& value) { + testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value)); + } +}; + +TEST_F(FlattenableHelpersTest, TriviallyCopyable) { + testTriviallyCopyable(42); + testTriviallyCopyable(1LL << 63); + testTriviallyCopyable(false); + testTriviallyCopyable(true); + testTriviallyCopyable(std::optional<int>()); + testTriviallyCopyable(std::optional<int>(4)); +} + +TEST_F(FlattenableHelpersTest, String) { + testWriteThenRead(std::string("Android")); + testWriteThenRead(std::string()); +} + +TEST_F(FlattenableHelpersTest, Vector) { + testWriteThenRead(std::vector<int>({1, 2, 3})); + testWriteThenRead(std::vector<int>()); +} + +TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) { + std::vector<size_t> buffer; + constexpr int kInternalValue = 16; + { + std::optional<TestLightFlattenable> value = + TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)}; + buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0); + void* rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + } + + const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size_t size = buffer.size(); + std::optional<TestLightFlattenable> valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + ASSERT_TRUE(valueRead.has_value()); + EXPECT_EQ(kInternalValue, *valueRead->ptr); +} + +TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) { + std::vector<size_t> buffer; + { + std::optional<TestLightFlattenable> value; + buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0); + void* rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + } + + const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size_t size = buffer.size(); + std::optional<TestLightFlattenable> valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + ASSERT_FALSE(valueRead.has_value()); +} + +} // namespace +} // namespace android diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h index 0556ea1342..1f1bcb3950 100644 --- a/opengl/include/EGL/eglext_angle.h +++ b/opengl/include/EGL/eglext_angle.h @@ -4,12 +4,12 @@ // found in the LICENSE file. // // eglext_angle.h: ANGLE modifications to the eglext.h header file. -// Currently we don't include this file directly, we patch eglext.h -// to include it implicitly so it is visible throughout our code. #ifndef INCLUDE_EGL_EGLEXT_ANGLE_ #define INCLUDE_EGL_EGLEXT_ANGLE_ +#include <EGL/eglext.h> + // clang-format off #ifndef EGL_ANGLE_robust_resource_initialization diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index e8d3684e4e..3c76c62f05 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -102,11 +102,6 @@ cc_defaults { "libbacktrace", "libbase", ], - target: { - vendor: { - exclude_shared_libs: ["libgraphicsenv"], - }, - }, } cc_library_static { diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index d66ef2b969..85e2c15c89 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -17,27 +17,23 @@ //#define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <EGL/Loader.h> - -#include <string> - -#include <dirent.h> -#include <dlfcn.h> +#include "EGL/Loader.h" #include <android-base/properties.h> #include <android/dlext.h> +#include <dirent.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> #include <log/log.h> #include <utils/Timers.h> - -#ifndef __ANDROID_VNDK__ -#include <graphicsenv/GraphicsEnv.h> -#endif #include <vndksupport/linker.h> +#include <string> + +#include "EGL/eglext_angle.h" #include "egl_platform_entries.h" #include "egl_trace.h" #include "egldefs.h" -#include <EGL/eglext_angle.h> namespace android { @@ -159,13 +155,11 @@ static bool should_unload_system_driver(egl_connection_t* cnx) { return true; } -#ifndef __ANDROID_VNDK__ // Return true if updated driver namespace is set. ns = android::GraphicsEnv::getInstance().getDriverNamespace(); if (ns) { return true; } -#endif return false; } @@ -276,7 +270,7 @@ void* Loader::open(egl_connection_t* cnx) // will set cnx->useAngle appropriately. // Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native), // not just loading ANGLE as option. - init_angle_backend(hnd->dso[0], cnx); + init_angle_backend(hnd->dso[2], cnx); } LOG_ALWAYS_FATAL_IF(!hnd, @@ -557,12 +551,8 @@ Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) { } void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) { - void* eglCreateDeviceANGLE = nullptr; - - ALOGV("dso: %p", dso); - eglCreateDeviceANGLE = dlsym(dso, "eglCreateDeviceANGLE"); - ALOGV("eglCreateDeviceANGLE: %p", eglCreateDeviceANGLE); - if (eglCreateDeviceANGLE) { + void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform"); + if (pANGLEGetDisplayPlatform) { ALOGV("ANGLE GLES library in use"); cnx->useAngle = true; } else { @@ -573,7 +563,7 @@ void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) { Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) { ATRACE_CALL(); -#ifndef __ANDROID_VNDK__ + android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace(); if (!ns) { return nullptr; @@ -603,9 +593,6 @@ Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) hnd->set(dso, GLESv2); } return hnd; -#else - return nullptr; -#endif } Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 3b1cf712a2..8c6f2842cc 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -19,25 +19,22 @@ #include "egl_display.h" -#include "../egl_impl.h" - -#include <EGL/eglext_angle.h> -#include <private/EGL/display.h> - -#include "Loader.h" -#include "egl_angle_platform.h" -#include "egl_cache.h" -#include "egl_object.h" -#include "egl_tls.h" - #include <SurfaceFlingerProperties.h> #include <android-base/properties.h> #include <android/dlext.h> +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <configstore/Utils.h> #include <dlfcn.h> #include <graphicsenv/GraphicsEnv.h> -#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> -#include <configstore/Utils.h> +#include "../egl_impl.h" +#include "EGL/eglext_angle.h" +#include "Loader.h" +#include "egl_angle_platform.h" +#include "egl_cache.h" +#include "egl_object.h" +#include "egl_tls.h" +#include "private/EGL/display.h" using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index aa24e8ee68..1119e4abdc 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -18,36 +18,32 @@ #include "egl_platform_entries.h" -#include <ctype.h> -#include <dlfcn.h> -#include <stdlib.h> -#include <string.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <EGL/eglext_angle.h> - #include <android-base/properties.h> #include <android-base/strings.h> #include <android/hardware_buffer.h> -#include <graphicsenv/GraphicsEnv.h> -#include <private/android/AHardwareBufferHelpers.h> - +#include <ctype.h> #include <cutils/compiler.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> #include <log/log.h> +#include <private/android/AHardwareBufferHelpers.h> +#include <stdlib.h> +#include <string.h> #include <condition_variable> #include <deque> #include <mutex> -#include <unordered_map> #include <string> #include <thread> +#include <unordered_map> #include "../egl_impl.h" - +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "EGL/eglext_angle.h" #include "egl_display.h" -#include "egl_object.h" #include "egl_layers.h" +#include "egl_object.h" #include "egl_tls.h" #include "egl_trace.h" @@ -2248,15 +2244,8 @@ EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface, } EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) { - // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus - // this function cannot be implemented when this libEGL is built for - // vendors. -#ifndef __ANDROID_VNDK__ if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer)); -#else - return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); -#endif } // ---------------------------------------------------------------------------- diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index 6eed24a17b..04fe1e6502 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -21,6 +21,7 @@ cc_defaults { "libbinder", "libcutils", "libgfxstats", + "libgpumem", "libgraphicsenv", "liblog", "libutils", @@ -85,6 +86,10 @@ cc_binary { name: "gpuservice", defaults: ["libgpuservice_binary"], init_rc: ["gpuservice.rc"], + required: [ + "bpfloader", + "gpu_mem.o", + ], srcs: [":gpuservice_binary_sources"], shared_libs: [ "libgpuservice", diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk new file mode 100644 index 0000000000..482fc6dfc3 --- /dev/null +++ b/services/gpuservice/CleanSpec.mk @@ -0,0 +1,52 @@ +# Copyright 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# Remove gpu_mem.o +$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp) diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index 304f1d059e..bad56376df 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -24,11 +24,11 @@ #include <binder/Parcel.h> #include <binder/PermissionCache.h> #include <cutils/properties.h> +#include <gpumem/GpuMem.h> #include <gpustats/GpuStats.h> #include <private/android_filesystem_config.h> #include <utils/String8.h> #include <utils/Trace.h> - #include <vkjson.h> namespace android { @@ -45,7 +45,10 @@ const String16 sDump("android.permission.DUMP"); const char* const GpuService::SERVICE_NAME = "gpu"; -GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){}; +GpuService::GpuService() + : mGpuMem(std::make_unique<GpuMem>()), mGpuStats(std::make_unique<GpuStats>()) { + mGpuMem->initialize(); +}; void GpuService::setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, @@ -98,6 +101,7 @@ status_t GpuService::doDump(int fd, const Vector<String16>& args, bool /*asProto } else { bool dumpAll = true; bool dumpDriverInfo = false; + bool dumpMem = false; bool dumpStats = false; size_t numArgs = args.size(); @@ -107,15 +111,21 @@ status_t GpuService::doDump(int fd, const Vector<String16>& args, bool /*asProto dumpStats = true; } else if (args[index] == String16("--gpudriverinfo")) { dumpDriverInfo = true; + } else if (args[index] == String16("--gpumem")) { + dumpMem = true; } } - dumpAll = !(dumpDriverInfo || dumpStats); + dumpAll = !(dumpDriverInfo || dumpMem || dumpStats); } if (dumpAll || dumpDriverInfo) { dumpGameDriverInfo(&result); result.append("\n"); } + if (dumpAll || dumpMem) { + mGpuMem->dump(args, &result); + result.append("\n"); + } if (dumpAll || dumpStats) { mGpuStats->dump(args, &result); result.append("\n"); diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h index ba44fe04d4..9a0460d2bc 100644 --- a/services/gpuservice/GpuService.h +++ b/services/gpuservice/GpuService.h @@ -28,6 +28,7 @@ namespace android { +class GpuMem; class GpuStats; class GpuService : public BnGpuService, public PriorityDumper { @@ -74,6 +75,7 @@ private: /* * Attributes */ + std::unique_ptr<GpuMem> mGpuMem; std::unique_ptr<GpuStats> mGpuStats; std::string developerDriverPath; }; diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp new file mode 100644 index 0000000000..b8758146cc --- /dev/null +++ b/services/gpuservice/bpfprogs/Android.bp @@ -0,0 +1,22 @@ +// Copyright 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +bpf { + name: "gpu_mem.o", + srcs: ["gpu_mem.c"], + cflags: [ + "-Wall", + "-Werror", + ], +} diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c new file mode 100644 index 0000000000..c75213bbd4 --- /dev/null +++ b/services/gpuservice/bpfprogs/gpu_mem.c @@ -0,0 +1,75 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <bpf_helpers.h> + +/* + * On Android the number of active processes using gpu is limited. + * So this is assumed to be true: SUM(num_procs_using_gpu[i]) <= 1024 + */ +#define GPU_MEM_TOTAL_MAP_SIZE 1024 + +/* + * This map maintains the global and per process gpu memory total counters. + * + * The KEY is ((gpu_id << 32) | pid) while VAL is the size in bytes. + * Use HASH type here since key is not int. + * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group. + */ +DEFINE_BPF_MAP_GRO(gpu_mem_total_map, HASH, uint64_t, uint64_t, GPU_MEM_TOTAL_MAP_SIZE, + AID_GRAPHICS); + +/* This struct aligns with the fields offsets of the raw tracepoint format */ +struct gpu_mem_total_args { + uint64_t ignore; + /* Actual fields start at offset 8 */ + uint32_t gpu_id; + uint32_t pid; + uint64_t size; +}; + +/* + * This program parses the gpu_mem/gpu_mem_total tracepoint's data into + * {KEY, VAL} pair used to update the corresponding bpf map. + * + * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group. + * Upon seeing size 0, the corresponding KEY needs to be cleaned up. + */ +DEFINE_BPF_PROG("tracepoint/gpu_mem/gpu_mem_total", AID_ROOT, AID_GRAPHICS, tp_gpu_mem_total) +(struct gpu_mem_total_args* args) { + uint64_t key = 0; + uint64_t cur_val = 0; + uint64_t* prev_val = NULL; + + /* The upper 32 bits are for gpu_id while the lower is the pid */ + key = ((uint64_t)args->gpu_id << 32) | args->pid; + cur_val = args->size; + + if (!cur_val) { + bpf_gpu_mem_total_map_delete_elem(&key); + return 0; + } + + prev_val = bpf_gpu_mem_total_map_lookup_elem(&key); + if (prev_val) { + *prev_val = cur_val; + } else { + bpf_gpu_mem_total_map_update_elem(&key, &cur_val, BPF_NOEXIST); + } + return 0; +} + +char _license[] SEC("license") = "Apache 2.0"; diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp new file mode 100644 index 0000000000..b2230b6c01 --- /dev/null +++ b/services/gpuservice/gpumem/Android.bp @@ -0,0 +1,41 @@ +// Copyright 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_shared { + name: "libgpumem", + srcs: [ + "GpuMem.cpp", + ], + shared_libs: [ + "libbase", + "libbpf", + "libbpf_android", + "libcutils", + "liblog", + "libutils", + ], + export_include_dirs: ["include"], + export_shared_lib_headers: [ + "libbase", + "libbpf_android", + ], + cppflags: [ + "-Wall", + "-Werror", + "-Wformat", + "-Wthread-safety", + "-Wunused", + "-Wunreachable-code", + ], +} diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp new file mode 100644 index 0000000000..1d4b524a7d --- /dev/null +++ b/services/gpuservice/gpumem/GpuMem.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "GpuMem" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "gpumem/GpuMem.h" + +#include <android-base/stringprintf.h> +#include <libbpf.h> +#include <libbpf_android.h> +#include <log/log.h> +#include <utils/Trace.h> + +#include <unordered_map> +#include <vector> + +namespace android { + +using base::StringAppendF; + +GpuMem::~GpuMem() { + bpf_detach_tracepoint(kGpuMemTraceGroup, kGpuMemTotalTracepoint); +} + +void GpuMem::initialize() { + // Make sure bpf programs are loaded + bpf::waitForProgsLoaded(); + + int fd = bpf::bpfFdGet(kGpuMemTotalProgPath, BPF_F_RDONLY); + if (fd < 0) { + ALOGE("Failed to retrieve pinned program from %s", kGpuMemTotalProgPath); + return; + } + + // Attach the program to the tracepoint, and the tracepoint is automatically enabled here. + if (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) { + ALOGE("Failed to attach bpf program to %s/%s tracepoint", kGpuMemTraceGroup, + kGpuMemTotalTracepoint); + return; + } + + // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map. + auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath); + if (!map.isValid()) { + ALOGE("Failed to create bpf map from %s", kGpuMemTotalMapPath); + return; + } + setGpuMemTotalMap(map); +} + +void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) { + mGpuMemTotalMap = std::move(map); +} + +// Dump the snapshots of global and per process memory usage on all gpus +void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) { + ATRACE_CALL(); + + if (!mGpuMemTotalMap.isValid()) { + result->append("Failed to initialize GPU memory eBPF\n"); + return; + } + + auto res = mGpuMemTotalMap.getFirstKey(); + if (!res.ok()) { + result->append("GPU memory total usage map is empty\n"); + return; + } + uint64_t key = res.value(); + // unordered_map<gpu_id, vector<pair<pid, size>>> + std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint64_t>>> dumpMap; + while (true) { + uint32_t gpu_id = key >> 32; + uint32_t pid = key; + + res = mGpuMemTotalMap.readValue(key); + if (!res.ok()) break; + uint64_t size = res.value(); + + dumpMap[gpu_id].emplace_back(pid, size); + + res = mGpuMemTotalMap.getNextKey(key); + if (!res.ok()) break; + key = res.value(); + } + + for (auto& gpu : dumpMap) { + if (gpu.second.empty()) continue; + StringAppendF(result, "Memory snapshot for GPU %u:\n", gpu.first); + + std::sort(gpu.second.begin(), gpu.second.end(), + [](auto& l, auto& r) { return l.first < r.first; }); + + int i = 0; + if (gpu.second[0].first != 0) { + StringAppendF(result, "Global total: N/A\n"); + } else { + StringAppendF(result, "Global total: %" PRIu64 "\n", gpu.second[0].second); + i++; + } + for (; i < gpu.second.size(); i++) { + StringAppendF(result, "Proc %u total: %" PRIu64 "\n", gpu.second[i].first, + gpu.second[i].second); + } + } +} + +} // namespace android diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h new file mode 100644 index 0000000000..6d0322ad1f --- /dev/null +++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h @@ -0,0 +1,56 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <bpf/BpfMap.h> +#include <utils/String16.h> +#include <utils/Vector.h> + +namespace android { + +class GpuMem { +public: + GpuMem() = default; + ~GpuMem(); + + // initialize eBPF program and map + void initialize(); + // dumpsys interface + void dump(const Vector<String16>& args, std::string* result); + +private: + // Friend class for testing. + friend class TestableGpuMem; + + // set gpu memory total map + void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map); + + // bpf map for GPU memory total data + android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap; + + // gpu memory tracepoint event category + static constexpr char kGpuMemTraceGroup[] = "gpu_mem"; + // gpu memory total tracepoint + static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total"; + // pinned gpu memory total bpf c program path in bpf sysfs + static constexpr char kGpuMemTotalProgPath[] = + "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total"; + // pinned gpu memory total bpf map path in bpf sysfs + static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map"; +}; + +} // namespace android diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp index 538506da77..db81f5db6d 100644 --- a/services/gpuservice/tests/unittests/Android.bp +++ b/services/gpuservice/tests/unittests/Android.bp @@ -19,11 +19,16 @@ cc_test { address: true, }, srcs: [ + "GpuMemTest.cpp", "GpuStatsTest.cpp", ], shared_libs: [ + "libbase", + "libbpf", + "libbpf_android", "libcutils", "libgfxstats", + "libgpumem", "libgraphicsenv", "liblog", "libstatslog", diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp new file mode 100644 index 0000000000..6ba304c9a9 --- /dev/null +++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp @@ -0,0 +1,146 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "gpuservice_unittest" + +#include <android-base/stringprintf.h> +#include <bpf/BpfMap.h> +#include <gmock/gmock.h> +#include <gpumem/GpuMem.h> +#include <gtest/gtest.h> +#include <inttypes.h> +#include <utils/String16.h> +#include <utils/Vector.h> + +#include "TestableGpuMem.h" + +namespace android { +namespace { + +using base::StringPrintf; +using testing::HasSubstr; + +constexpr uint32_t TEST_MAP_SIZE = 10; +constexpr uint64_t TEST_GLOBAL_KEY = 0; +constexpr uint64_t TEST_GLOBAL_VAL = 123; +constexpr uint64_t TEST_PROC_KEY_1 = 1; +constexpr uint64_t TEST_PROC_VAL_1 = 234; +constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2 +constexpr uint64_t TEST_PROC_VAL_2 = 345; + +class GpuMemTest : public testing::Test { +public: + GpuMemTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + ~GpuMemTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + void SetUp() override { + SKIP_IF_BPF_NOT_SUPPORTED; + ASSERT_EQ(0, bpf::setrlimitForTest()); + + mGpuMem = std::make_unique<GpuMem>(); + mTestableGpuMem = TestableGpuMem(mGpuMem.get()); + errno = 0; + mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, + BPF_F_NO_PREALLOC); + + EXPECT_EQ(0, errno); + EXPECT_LE(0, mTestMap.getMap().get()); + EXPECT_TRUE(mTestMap.isValid()); + } + + std::string dumpsys() { + std::string result; + Vector<String16> args; + mGpuMem->dump(args, &result); + return result; + } + + std::unique_ptr<GpuMem> mGpuMem; + TestableGpuMem mTestableGpuMem; + bpf::BpfMap<uint64_t, uint64_t> mTestMap; +}; + +TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) { + SKIP_IF_BPF_NOT_SUPPORTED; + + EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem"); + EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total"); + EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(), + "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total"); + EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map"); +} + +TEST_F(GpuMemTest, bpfInitializationFailed) { + SKIP_IF_BPF_NOT_SUPPORTED; + + EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n"); +} + +TEST_F(GpuMemTest, gpuMemTotalMapEmpty) { + SKIP_IF_BPF_NOT_SUPPORTED; + mTestableGpuMem.setGpuMemTotalMap(mTestMap); + + EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n"); +} + +TEST_F(GpuMemTest, globalMemTotal) { + SKIP_IF_BPF_NOT_SUPPORTED; + ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY)); + mTestableGpuMem.setGpuMemTotalMap(mTestMap); + + EXPECT_THAT(dumpsys(), HasSubstr(StringPrintf("Global total: %" PRIu64 "\n", TEST_GLOBAL_VAL))); +} + +TEST_F(GpuMemTest, missingGlobalMemTotal) { + SKIP_IF_BPF_NOT_SUPPORTED; + ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY)); + mTestableGpuMem.setGpuMemTotalMap(mTestMap); + + EXPECT_THAT(dumpsys(), HasSubstr("Global total: N/A")); +} + +TEST_F(GpuMemTest, procMemTotal) { + SKIP_IF_BPF_NOT_SUPPORTED; + ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY)); + ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY)); + mTestableGpuMem.setGpuMemTotalMap(mTestMap); + + EXPECT_THAT(dumpsys(), + HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n", + (uint32_t)(TEST_PROC_KEY_1 >> 32)))); + EXPECT_THAT(dumpsys(), + HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_1, + TEST_PROC_VAL_1))); + EXPECT_THAT(dumpsys(), + HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n", + (uint32_t)(TEST_PROC_KEY_2 >> 32)))); + EXPECT_THAT(dumpsys(), + HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_2, + TEST_PROC_VAL_2))); +} + +} // namespace +} // namespace android diff --git a/services/gpuservice/tests/unittests/TestableGpuMem.h b/services/gpuservice/tests/unittests/TestableGpuMem.h new file mode 100644 index 0000000000..0e4b01ca02 --- /dev/null +++ b/services/gpuservice/tests/unittests/TestableGpuMem.h @@ -0,0 +1,45 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <bpf/BpfMap.h> +#include <gpumem/GpuMem.h> + +namespace android { + +class TestableGpuMem { +public: + TestableGpuMem() = default; + explicit TestableGpuMem(GpuMem *gpuMem) : mGpuMem(gpuMem) {} + + void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) { + mGpuMem->setGpuMemTotalMap(map); + } + + std::string getGpuMemTraceGroup() { return mGpuMem->kGpuMemTraceGroup; } + + std::string getGpuMemTotalTracepoint() { return mGpuMem->kGpuMemTotalTracepoint; } + + std::string getGpuMemTotalProgPath() { return mGpuMem->kGpuMemTotalProgPath; } + + std::string getGpuMemTotalMapPath() { return mGpuMem->kGpuMemTotalMapPath; } + +private: + GpuMem *mGpuMem; +}; + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 3865f2960e..6900715438 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2092,7 +2092,7 @@ static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle, // If ownerPid is the same we don't generate occlusion events as there // is no in-process security boundary. return false; - } else if (otherInfo->isTrustedOverlay()) { + } else if (otherInfo->trustedOverlay) { return false; } else if (otherInfo->displayId != info->displayId) { return false; @@ -2109,7 +2109,7 @@ bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& break; // All future windows are below us. Exit early. } const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (canBeObscuredBy(windowHandle, otherHandle) && + if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y)) { return true; } @@ -2125,7 +2125,6 @@ bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& window if (windowHandle == otherHandle) { break; // All future windows are below us. Exit early. } - const InputWindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->overlaps(windowInfo)) { @@ -4150,6 +4149,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { "ms\n", windowInfo->ownerPid, windowInfo->ownerUid, ns2ms(windowInfo->dispatchingTimeout)); + dump += StringPrintf(INDENT4 " flags: %s\n", + inputWindowFlagsToString(windowInfo->layoutParamsFlags) + .c_str()); } } else { dump += INDENT2 "Windows: <none>\n"; diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index b0d3e3bde0..ec3dfc8faa 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -2,9 +2,14 @@ cc_library_shared { name: "libpowermanager", srcs: [ - "IPowerManager.cpp", - "Temperature.cpp", + "BatterySaverPolicyConfig.cpp", "CoolingDevice.cpp", + "PowerHalController.cpp", + "PowerHalLoader.cpp", + "PowerHalWrapper.cpp", + "PowerSaveState.cpp", + "Temperature.cpp", + "WorkSource.cpp", ":libpowermanager_aidl", ], @@ -17,9 +22,13 @@ cc_library_shared { }, shared_libs: [ - "libutils", "libbinder", - "liblog" + "libhidlbase", + "liblog", + "libutils", + "android.hardware.power@1.0", + "android.hardware.power@1.1", + "android.hardware.power-cpp", ], cflags: [ @@ -34,22 +43,3 @@ cc_library_shared { "include", ], } - -cc_test { - name: "thermalmanager-test", - srcs: ["IThermalManagerTest.cpp", - ], - cflags: [ - "-Wall", - "-Werror", - "-Wextra", - ], - shared_libs: [ - "libbase", - "libhidlbase", - "liblog", - "libpowermanager", - "libbinder", - "libutils", - ], -} diff --git a/services/powermanager/BatterySaverPolicyConfig.cpp b/services/powermanager/BatterySaverPolicyConfig.cpp new file mode 100644 index 0000000000..ee55b6b439 --- /dev/null +++ b/services/powermanager/BatterySaverPolicyConfig.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BatterySaverPolicyConfig" + +#include <android/BatterySaverPolicyConfig.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +status_t BatterySaverPolicyConfig::readDeviceSpecificSettings(const android::Parcel *parcel) { + int32_t num = 0; + status_t ret = parcel->readInt32(&num); + if (ret != OK) { + return ret; + } + for (int i = 0; i < num; i++) { + String16 key, val; + ret = parcel->readString16(&key) ?: + parcel->readString16(&val); + if (ret != OK) { + return ret; + } + mDeviceSpecificSettings.emplace_back(key, val); + } + return ret; +} + +status_t BatterySaverPolicyConfig::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->readFloat(&mAdjustBrightnessFactor) + ?: parcel->readBool(&mAdvertiseIsEnabled) + ?: parcel->readBool(&mDeferFullBackup) + ?: parcel->readBool(&mDeferKeyValueBackup) + ?: readDeviceSpecificSettings(parcel) + ?: parcel->readBool(&mDisableAnimation) + ?: parcel->readBool(&mDisableAod) + ?: parcel->readBool(&mDisableLaunchBoost) + ?: parcel->readBool(&mDisableOptionalSensors) + ?: parcel->readBool(&mDisableSoundTrigger) + ?: parcel->readBool(&mDisableVibration) + ?: parcel->readBool(&mEnableAdjustBrightness) + ?: parcel->readBool(&mEnableDataSaver) + ?: parcel->readBool(&mEnableFirewall) + ?: parcel->readBool(&mEnableNightMode) + ?: parcel->readBool(&mEnableQuickDoze) + ?: parcel->readBool(&mForceAllAppsStandby) + ?: parcel->readBool(&mForceBackgroundCheck) + ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode)); +} + +status_t BatterySaverPolicyConfig::writeDeviceSpecificSettings(android::Parcel *parcel) const { + status_t ret = parcel->writeInt32(mDeviceSpecificSettings.size()); + if (ret != OK) { + return ret; + } + for (auto& settings : mDeviceSpecificSettings) { + ret = parcel->writeString16(settings.first) ?: + parcel->writeString16(settings.second); + if (ret != OK) { + return ret; + } + } + return ret; +} + +status_t BatterySaverPolicyConfig::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->writeFloat(mAdjustBrightnessFactor) + ?: parcel->writeBool(mAdvertiseIsEnabled) + ?: parcel->writeBool(mDeferFullBackup) + ?: parcel->writeBool(mDeferKeyValueBackup) + ?: writeDeviceSpecificSettings(parcel) + ?: parcel->writeBool(mDisableAnimation) + ?: parcel->writeBool(mDisableAod) + ?: parcel->writeBool(mDisableLaunchBoost) + ?: parcel->writeBool(mDisableOptionalSensors) + ?: parcel->writeBool(mDisableSoundTrigger) + ?: parcel->writeBool(mDisableVibration) + ?: parcel->writeBool(mEnableAdjustBrightness) + ?: parcel->writeBool(mEnableDataSaver) + ?: parcel->writeBool(mEnableFirewall) + ?: parcel->writeBool(mEnableNightMode) + ?: parcel->writeBool(mEnableQuickDoze) + ?: parcel->writeBool(mForceAllAppsStandby) + ?: parcel->writeBool(mForceBackgroundCheck) + ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode)); +} + +} // namespace android::os diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp deleted file mode 100644 index ea3a831c13..0000000000 --- a/services/powermanager/IPowerManager.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "IPowerManager" -//#define LOG_NDEBUG 0 -#include <utils/Log.h> - -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> - -#include <powermanager/IPowerManager.h> - -namespace android { - -class BpPowerManager : public BpInterface<IPowerManager> -{ -public: - explicit BpPowerManager(const sp<IBinder>& impl) - : BpInterface<IPowerManager>(impl) - { - } - - virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag, - const String16& packageName, bool isOneWay) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - - data.writeStrongBinder(lock); - data.writeInt32(flags); - data.writeString16(tag); - data.writeString16(packageName); - data.writeInt32(0); // no WorkSource - data.writeString16(NULL, 0); // no history tag - return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply, - isOneWay ? IBinder::FLAG_ONEWAY : 0); - } - - virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag, - const String16& packageName, int uid, bool isOneWay) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - - data.writeStrongBinder(lock); - data.writeInt32(flags); - data.writeString16(tag); - data.writeString16(packageName); - data.writeInt32(uid); // uid to blame for the work - return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply, - isOneWay ? IBinder::FLAG_ONEWAY : 0); - } - - virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeStrongBinder(lock); - data.writeInt32(flags); - return remote()->transact(RELEASE_WAKE_LOCK, data, &reply, - isOneWay ? IBinder::FLAG_ONEWAY : 0); - } - - virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids, - bool isOneWay) { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeStrongBinder(lock); - data.writeInt32Array(len, uids); - return remote()->transact(UPDATE_WAKE_LOCK_UIDS, data, &reply, - isOneWay ? IBinder::FLAG_ONEWAY : 0); - } - - virtual status_t powerHint(int hintId, int param) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeInt32(hintId); - data.writeInt32(param); - // This FLAG_ONEWAY is in the .aidl, so there is no way to disable it - return remote()->transact(POWER_HINT, data, &reply, IBinder::FLAG_ONEWAY); - } - - virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeInt64(event_time_ms); - data.writeInt32(reason); - data.writeInt32(flags); - return remote()->transact(GO_TO_SLEEP, data, &reply, 0); - } - - virtual status_t reboot(bool confirm, const String16& reason, bool wait) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeInt32(confirm); - data.writeString16(reason); - data.writeInt32(wait); - return remote()->transact(REBOOT, data, &reply, 0); - } - - virtual status_t shutdown(bool confirm, const String16& reason, bool wait) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeInt32(confirm); - data.writeString16(reason); - data.writeInt32(wait); - return remote()->transact(SHUTDOWN, data, &reply, 0); - } - - virtual status_t crash(const String16& message) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeString16(message); - return remote()->transact(CRASH, data, &reply, 0); - } -}; - -IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager"); - -// ---------------------------------------------------------------------------- - -}; // namespace android diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp new file mode 100644 index 0000000000..178f545b2e --- /dev/null +++ b/services/powermanager/PowerHalController.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalController" +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <powermanager/PowerHalController.h> +#include <powermanager/PowerHalLoader.h> +#include <utils/Log.h> + +using namespace android::hardware::power; + +namespace android { + +namespace power { + +// ------------------------------------------------------------------------------------------------- + +std::unique_ptr<HalWrapper> HalConnector::connect() { + sp<IPower> halAidl = PowerHalLoader::loadAidl(); + if (halAidl) { + return std::make_unique<AidlHalWrapper>(halAidl); + } + sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0(); + sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1(); + if (halHidlV1_1) { + return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1); + } + if (halHidlV1_0) { + return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0); + } + return nullptr; +} + +void HalConnector::reset() { + PowerHalLoader::unloadAll(); +} + +// ------------------------------------------------------------------------------------------------- + +void PowerHalController::init() { + initHal(); +} + +// Check validity of current handle to the power HAL service, and create a new +// one if necessary. +std::shared_ptr<HalWrapper> PowerHalController::initHal() { + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + if (mConnectedHal == nullptr) { + mConnectedHal = mHalConnector->connect(); + if (mConnectedHal == nullptr) { + // Unable to connect to Power HAL service. Fallback to default. + return mDefaultHal; + } + } + return mConnectedHal; +} + +// Check if a call to Power HAL function failed; if so, log the failure and +// invalidate the current Power HAL handle. +HalResult PowerHalController::processHalResult(HalResult result, const char* fnName) { + if (result == HalResult::FAILED) { + ALOGE("%s() failed: power HAL service not available.", fnName); + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + // Drop Power HAL handle. This will force future api calls to reconnect. + mConnectedHal = nullptr; + mHalConnector->reset(); + } + return result; +} + +HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) { + std::shared_ptr<HalWrapper> handle = initHal(); + auto result = handle->setBoost(boost, durationMs); + return processHalResult(result, "setBoost"); +} + +HalResult PowerHalController::setMode(Mode mode, bool enabled) { + std::shared_ptr<HalWrapper> handle = initHal(); + auto result = handle->setMode(mode, enabled); + return processHalResult(result, "setMode"); +} + +} // namespace power + +} // namespace android diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp new file mode 100644 index 0000000000..1f1b43a607 --- /dev/null +++ b/services/powermanager/PowerHalLoader.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalLoader" + +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/IPower.h> +#include <binder/IServiceManager.h> +#include <hardware/power.h> +#include <hardware_legacy/power.h> +#include <powermanager/PowerHalLoader.h> + +using namespace android::hardware::power; + +namespace android { + +namespace power { + +// ------------------------------------------------------------------------------------------------- + +template <typename T, typename F> +sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) { + if (!exists) { + return nullptr; + } + if (hal) { + return hal; + } + hal = loadFn(); + if (hal) { + ALOGV("Successfully connected to Power HAL %s service.", halName); + } else { + ALOGV("Power HAL %s service not available.", halName); + exists = false; + } + return hal; +} + +// ------------------------------------------------------------------------------------------------- + +std::mutex PowerHalLoader::gHalMutex; +sp<IPower> PowerHalLoader::gHalAidl = nullptr; +sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr; +sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr; + +void PowerHalLoader::unloadAll() { + std::lock_guard<std::mutex> lock(gHalMutex); + gHalAidl = nullptr; + gHalHidlV1_0 = nullptr; + gHalHidlV1_1 = nullptr; +} + +sp<IPower> PowerHalLoader::loadAidl() { + std::lock_guard<std::mutex> lock(gHalMutex); + static bool gHalExists = true; + static auto loadFn = []() { return waitForVintfService<IPower>(); }; + return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL"); +} + +sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() { + std::lock_guard<std::mutex> lock(gHalMutex); + return loadHidlV1_0Locked(); +} + +sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() { + std::lock_guard<std::mutex> lock(gHalMutex); + static bool gHalExists = true; + static auto loadFn = []() { return V1_1::IPower::castFrom(loadHidlV1_0Locked()); }; + return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1"); +} + +sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() { + static bool gHalExists = true; + static auto loadFn = []() { return V1_0::IPower::getService(); }; + return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0"); +} + +// ------------------------------------------------------------------------------------------------- + +} // namespace power + +} // namespace android diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp new file mode 100644 index 0000000000..5f4bfed595 --- /dev/null +++ b/services/powermanager/PowerHalWrapper.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "HalWrapper" +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/Mode.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +using namespace android::hardware::power; + +namespace android { + +namespace power { + +// ------------------------------------------------------------------------------------------------- + +inline HalResult toHalResult(const binder::Status& result) { + return result.isOk() ? HalResult::SUCCESSFUL : HalResult::FAILED; +} + +template <typename T> +inline HalResult toHalResult(const hardware::Return<T>& result) { + return result.isOk() ? HalResult::SUCCESSFUL : HalResult::FAILED; +} + +// ------------------------------------------------------------------------------------------------- + +HalResult EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) { + ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available", + toString(boost).c_str(), durationMs); + return HalResult::UNSUPPORTED; +} + +HalResult EmptyHalWrapper::setMode(Mode mode, bool enabled) { + ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(), + enabled ? "true" : "false"); + return HalResult::UNSUPPORTED; +} + +// ------------------------------------------------------------------------------------------------- + +HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) { + if (boost == Boost::INTERACTION) { + return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs); + } else { + ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str()); + return HalResult::UNSUPPORTED; + } +} + +HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) { + uint32_t data = enabled ? 1 : 0; + switch (mode) { + case Mode::LAUNCH: + return sendPowerHint(V1_0::PowerHint::LAUNCH, data); + case Mode::LOW_POWER: + return sendPowerHint(V1_0::PowerHint::LOW_POWER, data); + case Mode::SUSTAINED_PERFORMANCE: + return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data); + case Mode::VR: + return sendPowerHint(V1_0::PowerHint::VR_MODE, data); + case Mode::INTERACTIVE: + return setInteractive(enabled); + case Mode::DOUBLE_TAP_TO_WAKE: + return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled); + default: + ALOGV("Skipped setMode %s because Power HAL AIDL not available", + toString(mode).c_str()); + return HalResult::UNSUPPORTED; + } +} + +HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) { + return toHalResult(mHandleV1_0->powerHint(hintId, data)); +} + +HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) { + return toHalResult(mHandleV1_0->setInteractive(enabled)); +} + +HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) { + return toHalResult(mHandleV1_0->setFeature(feature, enabled)); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) { + return toHalResult(mHandleV1_1->powerHintAsync(hintId, data)); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) { + std::unique_lock<std::mutex> lock(mBoostMutex); + // Quick return if boost is not supported by HAL + if (boost > Boost::DISPLAY_UPDATE_IMMINENT || + mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) { + ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str()); + return HalResult::UNSUPPORTED; + } + + if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) { + bool isSupported = false; + auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported); + if (!isSupportedRet.isOk()) { + ALOGV("Skipped setBoost %s because Power HAL is not available to check " + "support", + toString(boost).c_str()); + return HalResult::FAILED; + } + + mBoostSupportedArray[static_cast<int32_t>(boost)] = + isSupported ? HalSupport::ON : HalSupport::OFF; + if (!isSupported) { + ALOGV("Skipped setBoost %s because Power HAL doesn't support it", + toString(boost).c_str()); + return HalResult::UNSUPPORTED; + } + } + lock.unlock(); + + return toHalResult(mHandle->setBoost(boost, durationMs)); +} + +HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) { + std::unique_lock<std::mutex> lock(mModeMutex); + // Quick return if mode is not supported by HAL + if (mode > Mode::DISPLAY_INACTIVE || + mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) { + ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str()); + return HalResult::UNSUPPORTED; + } + + if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) { + bool isSupported = false; + auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported); + if (!isSupportedRet.isOk()) { + ALOGV("Skipped setMode %s because Power HAL is not available to check " + "support", + toString(mode).c_str()); + return HalResult::FAILED; + } + + mModeSupportedArray[static_cast<int32_t>(mode)] = + isSupported ? HalSupport::ON : HalSupport::OFF; + if (!isSupported) { + ALOGV("Skipped setMode %s because Power HAL doesn't support it", + toString(mode).c_str()); + return HalResult::UNSUPPORTED; + } + } + lock.unlock(); + + return toHalResult(mHandle->setMode(mode, enabled)); +} + +// ------------------------------------------------------------------------------------------------- + +} // namespace power + +} // namespace android diff --git a/services/powermanager/PowerSaveState.cpp b/services/powermanager/PowerSaveState.cpp new file mode 100644 index 0000000000..6d1830a887 --- /dev/null +++ b/services/powermanager/PowerSaveState.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerSaveState" + +#include <android/PowerSaveState.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +status_t PowerSaveState::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->readBool(&mBatterySaverEnabled) + ?: parcel->readBool(&mGlobalBatterySaverEnabled) + ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode)) + ?: parcel->readFloat(&mBrightnessFactor); +} + +status_t PowerSaveState::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->writeBool(mBatterySaverEnabled) + ?: parcel->writeBool(mGlobalBatterySaverEnabled) + ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode)) + ?: parcel->writeFloat(mBrightnessFactor); +} + +} // namespace android::os diff --git a/services/powermanager/TEST_MAPPING b/services/powermanager/TEST_MAPPING new file mode 100644 index 0000000000..caaec556e9 --- /dev/null +++ b/services/powermanager/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libpowermanager_test" + } + ] +} diff --git a/services/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp new file mode 100644 index 0000000000..1006a0666f --- /dev/null +++ b/services/powermanager/WorkSource.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "WorkSource" + +#include <android/WorkSource.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +status_t WorkSource::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + int32_t num; + status_t ret = parcel->readInt32(&num) + ?: parcel->readInt32Vector(&mUids) + ?: parcel->readString16Vector(&mNames); + + return ret; +} + +status_t WorkSource::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->writeInt32(mUids.size()) + ?: parcel->writeInt32Vector(mUids) + ?: parcel->writeString16Vector(mNames); +} + +} // namespace android::os diff --git a/services/powermanager/include/android/BatterySaverPolicyConfig.h b/services/powermanager/include/android/BatterySaverPolicyConfig.h new file mode 100644 index 0000000000..728c8a02a0 --- /dev/null +++ b/services/powermanager/include/android/BatterySaverPolicyConfig.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H +#define ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H + +#include <math.h> +#include <binder/Parcelable.h> +#include <utils/RefBase.h> + +namespace android::os { + +enum class LocationMode : int32_t; +/** + * BatterySaverPolicyConfig is a structure of configs to set Battery Saver policy flags. + * This file needs to be kept in sync with + * frameworks/base/core/java/android/os/BatterySaverPolicyConfig.java + */ +struct BatterySaverPolicyConfig : public android::Parcelable { + + BatterySaverPolicyConfig(float adjustBrightnessFactor = 1.0f, + bool advertiseIsEnabled = false, + bool deferFullBackup = false, + bool deferKeyValueBackup = false, + std::vector<std::pair<String16, String16>> deviceSpecificSettings = {}, + bool disableAnimation = false, + bool disableAod = false, + bool disableLaunchBoost = false, + bool disableOptionalSensors = false, + bool disableSoundTrigger = false, + bool disableVibration = false, + bool enableAdjustBrightness = false, + bool enableDataSaver = false, + bool enableFirewall = false, + bool enableNightMode = false, + bool enableQuickDoze = false, + bool forceAllAppsStandby = false, + bool forceBackgroundCheck = false, + LocationMode locationMode = static_cast<LocationMode>(0)) + : mAdjustBrightnessFactor(adjustBrightnessFactor), + mAdvertiseIsEnabled(advertiseIsEnabled), + mDeferFullBackup(deferFullBackup), + mDeferKeyValueBackup(deferKeyValueBackup), + mDeviceSpecificSettings(deviceSpecificSettings), + mDisableAnimation(disableAnimation), + mDisableAod(disableAod), + mDisableLaunchBoost(disableLaunchBoost), + mDisableOptionalSensors(disableOptionalSensors), + mDisableSoundTrigger(disableSoundTrigger), + mDisableVibration(disableVibration), + mEnableAdjustBrightness(enableAdjustBrightness), + mEnableDataSaver(enableDataSaver), + mEnableFirewall(enableFirewall), + mEnableNightMode(enableNightMode), + mEnableQuickDoze(enableQuickDoze), + mForceAllAppsStandby(forceAllAppsStandby), + mForceBackgroundCheck(forceBackgroundCheck), + mLocationMode(locationMode) { + } + + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + bool operator == (const BatterySaverPolicyConfig &bsp) const { + return fabs(mAdjustBrightnessFactor - bsp.mAdjustBrightnessFactor) == 0.0f && + mAdvertiseIsEnabled == bsp.mAdvertiseIsEnabled && + mDeferFullBackup == bsp.mDeferFullBackup && + mDeferKeyValueBackup == bsp.mDeferKeyValueBackup && + mDeviceSpecificSettings == bsp.mDeviceSpecificSettings && + mDisableAnimation == bsp.mDisableAnimation && + mDisableAod == bsp.mDisableAod && + mDisableLaunchBoost == bsp.mDisableLaunchBoost && + mDisableOptionalSensors == bsp.mDisableOptionalSensors && + mDisableSoundTrigger == bsp.mDisableSoundTrigger && + mDisableVibration == bsp.mDisableVibration && + mEnableAdjustBrightness == bsp.mEnableAdjustBrightness && + mEnableDataSaver == bsp.mEnableDataSaver && + mEnableFirewall == bsp.mEnableFirewall && + mEnableNightMode == bsp.mEnableNightMode && + mEnableQuickDoze == bsp.mEnableQuickDoze && + mForceAllAppsStandby == bsp.mForceAllAppsStandby && + mForceBackgroundCheck == bsp.mForceBackgroundCheck && + mLocationMode == bsp.mLocationMode; + } + +private: + status_t readDeviceSpecificSettings(const android::Parcel *parcel); + status_t writeDeviceSpecificSettings(android::Parcel *parcel) const; + /** Adjust screen brightness factor */ + float mAdjustBrightnessFactor; + /** Is advertise enabled */ + bool mAdvertiseIsEnabled; + /** Defer full backup */ + bool mDeferFullBackup; + /** Defer key value backup */ + bool mDeferKeyValueBackup; + /** Device specific settings */ + std::vector<std::pair<String16, String16>> mDeviceSpecificSettings; + /** Disable animation */ + bool mDisableAnimation; + /** Disable Aod */ + bool mDisableAod; + /** Disable launch boost */ + bool mDisableLaunchBoost; + /** Disable optional sensors */ + bool mDisableOptionalSensors; + /** Disable sound trigger */ + bool mDisableSoundTrigger; + /** Disable vibration */ + bool mDisableVibration; + /** Enable adjust brightness */ + bool mEnableAdjustBrightness; + /** Enable data saver */ + bool mEnableDataSaver; + /** Enable firewall */ + bool mEnableFirewall; + /** Enable night mode */ + bool mEnableNightMode; + /** Enable quick doze */ + bool mEnableQuickDoze; + /** Force all Apps standby */ + bool mForceAllAppsStandby; + /** Force Background check */ + bool mForceBackgroundCheck; + /** Location mode */ + LocationMode mLocationMode; +}; + +} // namespace android::os + +#endif /* ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H */ diff --git a/services/powermanager/include/android/LocationMode.h b/services/powermanager/include/android/LocationMode.h new file mode 100644 index 0000000000..42933d438b --- /dev/null +++ b/services/powermanager/include/android/LocationMode.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_LOCATION_MODE_H +#define ANDROID_OS_LOCATION_MODE_H + +namespace android::os { + +enum class LocationMode : int32_t { + NO_CHANGE = IPowerManager::LOCATION_MODE_NO_CHANGE, + GPS_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF, + ALL_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF, + FOREGROUND_ONLY = IPowerManager::LOCATION_MODE_FOREGROUND_ONLY, + THROTTLE_REQUESTS_WHEN_SCREEN_OFF = + IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF, + MIN = IPowerManager::LOCATION_MODE_NO_CHANGE, + MAX = IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF, +}; + +} // namespace android::os + +#endif /* ANDROID_OS_LOCATION_MODE_H */ diff --git a/services/powermanager/include/android/PowerSaveState.h b/services/powermanager/include/android/PowerSaveState.h new file mode 100644 index 0000000000..b421f6aaa8 --- /dev/null +++ b/services/powermanager/include/android/PowerSaveState.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_POWER_SAVE_STATE_H +#define ANDROID_OS_POWER_SAVE_STATE_H + +#include <math.h> +#include <binder/Parcelable.h> +#include <utils/RefBase.h> + +namespace android::os { + +enum class LocationMode : int32_t; +/** + * PowerSaveState is a structure to encapsulate PowerSaveState status. + * This file needs to be kept in sync with frameworks/base/core/java/android/os/PowerSaveState.java + */ +struct PowerSaveState : public android::Parcelable { + + PowerSaveState(bool batterySaverEnabled = false, + bool globalBatterySaverEnabled = false, + LocationMode locationMode = static_cast<LocationMode>(0), + float brightnessFactor = 0.5f) + : mBatterySaverEnabled(batterySaverEnabled), + mGlobalBatterySaverEnabled(globalBatterySaverEnabled), + mLocationMode(locationMode), + mBrightnessFactor(brightnessFactor) { + } + + bool getBatterySaverEnabled() const { return mBatterySaverEnabled; } + bool getGlobalBatterySaverEnabled() const { return mGlobalBatterySaverEnabled; } + LocationMode getLocationMode() const { return mLocationMode; } + float getBrightnessFactor() const { return mBrightnessFactor; } + bool operator == (const PowerSaveState &ps) const { + return mBatterySaverEnabled == ps.mBatterySaverEnabled && + mGlobalBatterySaverEnabled == ps.mGlobalBatterySaverEnabled && + mLocationMode == ps.mLocationMode && + fabs(mBrightnessFactor - ps.mBrightnessFactor) == 0.0f; + } + + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + +private: + /** Whether we should enable battery saver for this service. */ + bool mBatterySaverEnabled; + /** Whether battery saver mode is enabled. */ + bool mGlobalBatterySaverEnabled; + /** Location mode */ + LocationMode mLocationMode; + /** Screen brightness factor. */ + float mBrightnessFactor; +}; + +} // namespace android::os + +#endif /* ANDROID_OS_POWER_SAVE_STATE_H */ diff --git a/services/powermanager/include/android/WorkSource.h b/services/powermanager/include/android/WorkSource.h new file mode 100644 index 0000000000..f12847d040 --- /dev/null +++ b/services/powermanager/include/android/WorkSource.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_WORKSOURCE_H +#define ANDROID_OS_WORKSOURCE_H + +#include <optional> +#include <binder/Parcelable.h> +#include <utils/RefBase.h> + +namespace android::os { + +/** + * WorkSource is a structure to describes the source of some work that may be done by someone else. + * This file needs to be kept in sync with frameworks/base/core/java/android/os/WorkSource.java + */ +struct WorkSource : public android::Parcelable { + WorkSource( + std::vector<int32_t> uids = {}, + std::optional<std::vector<std::optional<String16>>> names = std::nullopt) + : mUids(uids), + mNames(names) { + } + std::vector<int32_t> getUids() const { return mUids; } + std::optional<std::vector<std::optional<String16>>> getNames() const { return mNames; } + bool operator == (const WorkSource &ws) const { + return mUids == ws.mUids && mNames == ws.mNames; + } + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + +private: + /** WorkSource UID array */ + std::vector<int32_t> mUids = {}; + /** WorkSource Tag array */ + std::optional<std::vector<std::optional<String16>>> mNames = {}; +}; + +} // namespace android::os + +#endif /* ANDROID_OS_WORKSOURCE_H */ diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp new file mode 100644 index 0000000000..49abc1152b --- /dev/null +++ b/services/powermanager/tests/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libpowermanager_test", + test_suites: ["device-tests"], + srcs: [ + "IThermalManagerTest.cpp", + "PowerHalControllerTest.cpp", + "PowerHalLoaderTest.cpp", + "PowerHalWrapperAidlTest.cpp", + "PowerHalWrapperHidlV1_0Test.cpp", + "PowerHalWrapperHidlV1_1Test.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + shared_libs: [ + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libpowermanager", + "libutils", + "android.hardware.power@1.0", + "android.hardware.power@1.1", + "android.hardware.power-cpp", + ], + static_libs: [ + "libgmock", + ], +} diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp index 575b9ee1c4..575b9ee1c4 100644 --- a/services/powermanager/IThermalManagerTest.cpp +++ b/services/powermanager/tests/IThermalManagerTest.cpp diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp new file mode 100644 index 0000000000..141b2440b8 --- /dev/null +++ b/services/powermanager/tests/PowerHalControllerTest.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalControllerTest" + +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalController.h> +#include <utils/Log.h> + +#include <thread> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using android::hardware::power::V1_0::IPower; +using android::hardware::power::V1_0::PowerHint; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPowerV1_0 : public IPower { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); +}; + +class TestPowerHalConnector : public HalConnector { +public: + TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {} + virtual ~TestPowerHalConnector() = default; + + virtual std::unique_ptr<HalWrapper> connect() override { + mCountMutex.lock(); + ++mConnectedCount; + mCountMutex.unlock(); + return std::make_unique<HidlHalWrapperV1_0>(mHal); + } + + void reset() override { + mCountMutex.lock(); + ++mResetCount; + mCountMutex.unlock(); + } + + int getConnectCount() { return mConnectedCount; } + + int getResetCount() { return mResetCount; } + +private: + sp<IPower> mHal = nullptr; + std::mutex mCountMutex; + int mConnectedCount = 0; + int mResetCount = 0; +}; + +class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector { +public: + AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {} + + std::unique_ptr<HalWrapper> connect() override { + // Call parent to update counter, but ignore connected HalWrapper. + TestPowerHalConnector::connect(); + return nullptr; + } +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalControllerTest : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIPowerV1_0>(); + std::unique_ptr<TestPowerHalConnector> halConnector = + std::make_unique<TestPowerHalConnector>(mMockHal); + mHalConnector = halConnector.get(); + mHalController = std::make_unique<PowerHalController>(std::move(halConnector)); + } + +protected: + sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr; + TestPowerHalConnector* mHalConnector = nullptr; + std::unique_ptr<PowerHalController> mHalController = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + mHalController->init(); + mHalController->init(); + + // PowerHalConnector was called only once and never reset. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 1); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) { + std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector = + std::make_unique<AlwaysFailingTestPowerHalConnector>(); + AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get(); + PowerHalController halController(std::move(halConnector)); + + int powerHalConnectCount = failingHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + // Still works with EmptyPowerHalWrapper as fallback ignoring every api call + // and logging. + auto result = halController.setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + result = halController.setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + + // PowerHalConnector was called every time to attempt to reconnect with + // underlying service. + powerHalConnectCount = failingHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 2); + // PowerHalConnector was never reset. + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + { + InSequence seg; + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1)); + } + + auto result = mHalController->setBoost(Boost::INTERACTION, 100); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mHalController->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + + // PowerHalConnector was called only once and never reset. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 1); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _)) + .WillByDefault([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4)); + + auto result = mHalController->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mHalController->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::FAILED, result); + result = mHalController->setMode(Mode::VR, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mHalController->setMode(Mode::LOW_POWER, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + + // PowerHalConnector was called only twice: on first api call and after failed + // call. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 2); + // PowerHalConnector was reset once after failed call. + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 1); +} + +TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + + // PowerHalConnector was called only once and never reset. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 1); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(10)); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mHalController->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + // PowerHalConnector was called only by the first thread to use the api and + // never reset. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 1); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _)) + .WillByDefault([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(40)); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mHalController->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + threads.push_back(std::thread([&]() { + auto result = mHalController->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::FAILED, result); + })); + threads.push_back(std::thread([&]() { + auto result = mHalController->setMode(Mode::LOW_POWER, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + threads.push_back(std::thread([&]() { + auto result = mHalController->setMode(Mode::VR, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + // PowerHalConnector was called at least once by the first thread. + // Reset and reconnect calls were made at most 10 times, once after each + // failure. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11))); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_THAT(powerHalResetCount, Le(10)); +} diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp new file mode 100644 index 0000000000..058e1b5ca8 --- /dev/null +++ b/services/powermanager/tests/PowerHalLoaderTest.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalLoaderTest" + +#include <android-base/logging.h> +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/IPower.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalLoader.h> + +#include <future> + +using IPowerV1_0 = android::hardware::power::V1_0::IPower; +using IPowerV1_1 = android::hardware::power::V1_1::IPower; +using IPowerAidl = android::hardware::power::IPower; + +using namespace android; +using namespace android::power; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +template <typename T> +sp<T> loadHal(); + +template <> +sp<IPowerAidl> loadHal<IPowerAidl>() { + return PowerHalLoader::loadAidl(); +} + +template <> +sp<IPowerV1_0> loadHal<IPowerV1_0>() { + return PowerHalLoader::loadHidlV1_0(); +} + +template <> +sp<IPowerV1_1> loadHal<IPowerV1_1>() { + return PowerHalLoader::loadHidlV1_1(); +} + +// ------------------------------------------------------------------------------------------------- + +template <typename T> +class PowerHalLoaderTest : public Test { +public: + sp<T> load() { return ::loadHal<T>(); } + void unload() { PowerHalLoader::unloadAll(); } +}; + +// ------------------------------------------------------------------------------------------------- + +typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes; +TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes); + +TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) { + sp<TypeParam> firstHal = this->load(); + if (firstHal == nullptr) { + ALOGE("Power HAL not available. Skipping test."); + return; + } + sp<TypeParam> secondHal = this->load(); + ASSERT_EQ(firstHal, secondHal); +} + +TYPED_TEST(PowerHalLoaderTest, TestUnload) { + sp<TypeParam> firstHal = this->load(); + if (firstHal == nullptr) { + ALOGE("Power HAL not available. Skipping test."); + return; + } + this->unload(); + sp<TypeParam> secondHal = this->load(); + ASSERT_NE(secondHal, nullptr); + ASSERT_NE(firstHal, secondHal); +} + +TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) { + std::vector<std::future<sp<TypeParam>>> futures; + for (int i = 0; i < 10; i++) { + futures.push_back( + std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this)); + } + + futures[0].wait(); + sp<TypeParam> firstHal = futures[0].get(); + if (firstHal == nullptr) { + ALOGE("Power HAL not available. Skipping test."); + return; + } + + for (int i = 1; i < 10; i++) { + futures[i].wait(); + sp<TypeParam> currentHal = futures[i].get(); + ASSERT_EQ(firstHal, currentHal); + } +} diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp new file mode 100644 index 0000000000..a7656598f0 --- /dev/null +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalWrapperAidlTest" + +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/Mode.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +#include <thread> + +using android::binder::Status; +using android::hardware::power::Boost; +using android::hardware::power::IPower; +using android::hardware::power::Mode; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPower : public IPower { +public: + MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override)); + MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override)); + MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override)); + MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override)); + MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); + MOCK_METHOD(std::string, getInterfaceHash, (), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalWrapperAidlTest : public Test { +public: + void SetUp() override; + +protected: + std::unique_ptr<HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIPower>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +void PowerHalWrapperAidlTest::SetUp() { + mMockHal = new StrictMock<MockIPower>(); + mWrapper = std::make_unique<AidlHalWrapper>(mMockHal); + ASSERT_NE(mWrapper, nullptr); +} + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalWrapperAidlTest, TestSetBoostSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + } + + auto result = mWrapper->setBoost(Boost::INTERACTION, 100); + ASSERT_EQ(HalResult::FAILED, result); + result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) { + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status()))); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10)); + } + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->setBoost(Boost::INTERACTION, 100); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetModeSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + } + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::FAILED, result); + result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) { + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status()))); + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10)); + } + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->setMode(Mode::LAUNCH, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); +} diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp new file mode 100644 index 0000000000..6693d0b070 --- /dev/null +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalWrapperHidlV1_0Test" + +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using android::hardware::power::V1_0::IPower; +using android::hardware::power::V1_0::PowerHint; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPowerV1_0 : public IPower { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalWrapperHidlV1_0Test : public Test { +public: + void SetUp() override; + +protected: + std::unique_ptr<HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +void PowerHalWrapperHidlV1_0Test::SetUp() { + mMockHal = new StrictMock<MockIPowerV1_0>(); + mWrapper = std::make_unique<HidlHalWrapperV1_0>(mMockHal); + ASSERT_NE(mWrapper, nullptr); +} + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostSuccessful) { + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1)); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) { + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) { + auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::LOW_POWER, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::VR, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::INTERACTIVE, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) { + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setMode(Mode::LAUNCH, 1); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) { + auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp new file mode 100644 index 0000000000..55bbd6df79 --- /dev/null +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalWrapperHidlV1_1Test" + +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using android::hardware::power::V1_0::PowerHint; +using IPowerV1_1 = android::hardware::power::V1_1::IPower; +using IPowerV1_0 = android::hardware::power::V1_0::IPower; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPowerV1_0 : public IPowerV1_0 { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); +}; + +class MockIPowerV1_1 : public IPowerV1_1 { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); + MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats, + (getSubsystemLowPowerStats_cb _hidl_cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalWrapperHidlV1_1Test : public Test { +public: + void SetUp() override; + +protected: + std::unique_ptr<HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr; + sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +void PowerHalWrapperHidlV1_1Test::SetUp() { + mMockHalV1_0 = new StrictMock<MockIPowerV1_0>(); + mMockHalV1_1 = new StrictMock<MockIPowerV1_1>(); + mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1); + ASSERT_NE(mWrapper, nullptr); +} + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) { + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000))) + .Times(Exactly(1)); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) { + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) { + auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) { + { + InSequence seq; + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_1.get(), + powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_0.get(), + setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::LOW_POWER, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::VR, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::INTERACTIVE, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) { + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setMode(Mode::LAUNCH, 1); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) { + auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 26e37aa102..3f88f7750b 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -699,6 +699,9 @@ status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) { return PERMISSION_DENIED; } + if (args.size() == 0) { + return BAD_INDEX; + } if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) { return BAD_VALUE; } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index e8f54f57b1..b07c904917 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -921,9 +921,8 @@ std::optional<base::unique_fd> Output::composeSurfaces( const nsecs_t renderEngineStart = systemTime(); status_t status = - renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, - buf->getNativeBuffer(), /*useFramebufferCache=*/true, - std::move(fd), &readyFence); + renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf, + /*useFramebufferCache=*/true, std::move(fd), &readyFence); if (status != NO_ERROR && mClientCompositionRequestCache) { // If rendering was not successful, remove the request from the cache. diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 730f29744a..9aa274bc23 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -116,6 +116,10 @@ void DisplayDevice::setDisplayName(const std::string& displayName) { } } +void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) { + mDeviceProductInfo = std::move(info); +} + uint32_t DisplayDevice::getPageFlipCount() const { return mCompositionDisplay->getRenderSurface()->getPageFlipCount(); } @@ -269,6 +273,12 @@ void DisplayDevice::dump(std::string& result) const { StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(), static_cast<int32_t>(mPowerMode)); StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value()); + StringAppendF(&result, "deviceProductInfo="); + if (mDeviceProductInfo) { + mDeviceProductInfo->dump(result); + } else { + result.append("{}"); + } getCompositionDisplay()->dump(result); } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index cb467ea292..81b3ccf70c 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -59,12 +59,14 @@ class Display; class DisplaySurface; } // namespace compositionengine -class DisplayDevice : public LightRefBase<DisplayDevice> { +class DisplayDevice : public RefBase { public: constexpr static float sDefaultMinLumiance = 0.0; constexpr static float sDefaultMaxLumiance = 500.0; explicit DisplayDevice(DisplayDeviceCreationArgs& args); + + // Must be destroyed on the main thread because it may call into HWComposer. virtual ~DisplayDevice(); std::shared_ptr<compositionengine::Display> getCompositionDisplay() const { @@ -136,6 +138,11 @@ public: void setDisplayName(const std::string& displayName); const std::string& getDisplayName() const { return mDisplayName; } + void setDeviceProductInfo(std::optional<DeviceProductInfo> info); + const std::optional<DeviceProductInfo>& getDeviceProductInfo() const { + return mDeviceProductInfo; + } + /* ------------------------------------------------------------------------ * Display power mode management. */ @@ -182,6 +189,8 @@ private: // TODO(b/74619554): Remove special cases for primary display. const bool mIsPrimary; + + std::optional<DeviceProductInfo> mDeviceProductInfo; }; struct DisplayDeviceState { @@ -189,7 +198,7 @@ struct DisplayDeviceState { DisplayId id; DisplayConnectionType type; hardware::graphics::composer::hal::HWDisplayId hwcDisplayId; - + std::optional<DeviceProductInfo> deviceProductInfo; bool operator==(const Physical& other) const { return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId; } @@ -239,21 +248,18 @@ struct DisplayDeviceCreationArgs { class DisplayRenderArea : public RenderArea { public: - DisplayRenderArea(const sp<const DisplayDevice>& display, - RotationFlags rotation = ui::Transform::ROT_0) - : DisplayRenderArea(display, display->getBounds(), - static_cast<uint32_t>(display->getWidth()), - static_cast<uint32_t>(display->getHeight()), - display->getCompositionDataSpace(), rotation) {} - - DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation, - bool allowSecureLayers = true) - : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace, - display->getViewport(), applyDeviceOrientation(rotation, display)), - mDisplay(std::move(display)), - mSourceCrop(sourceCrop), - mAllowSecureLayers(allowSecureLayers) {} + static std::unique_ptr<RenderArea> create(wp<const DisplayDevice> displayWeak, + const Rect& sourceCrop, ui::Size reqSize, + ui::Dataspace reqDataSpace, RotationFlags rotation, + bool allowSecureLayers = true) { + if (auto display = displayWeak.promote()) { + // Using new to access a private constructor. + return std::unique_ptr<DisplayRenderArea>( + new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace, + rotation, allowSecureLayers)); + } + return nullptr; + } const ui::Transform& getTransform() const override { return mTransform; } Rect getBounds() const override { return mDisplay->getBounds(); } @@ -300,6 +306,14 @@ public: } private: + DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, ui::Size reqSize, + ui::Dataspace reqDataSpace, RotationFlags rotation, bool allowSecureLayers) + : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getViewport(), + applyDeviceOrientation(rotation, display)), + mDisplay(std::move(display)), + mSourceCrop(sourceCrop), + mAllowSecureLayers(allowSecureLayers) {} + static RotationFlags applyDeviceOrientation(RotationFlags orientationFlag, const sp<const DisplayDevice>& device) { uint32_t inverseRotate90 = 0; diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp index 4dfc7431de..52a6380ebd 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp @@ -71,12 +71,8 @@ char getPnpLetter(uint16_t id) { DeviceProductInfo buildDeviceProductInfo(const Edid& edid) { DeviceProductInfo info; - std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin()); - info.name[edid.displayName.size()] = '\0'; - - const auto productId = std::to_string(edid.productId); - std::copy(productId.begin(), productId.end(), info.productId.begin()); - info.productId[productId.size()] = '\0'; + info.name.assign(edid.displayName); + info.productId = std::to_string(edid.productId); info.manufacturerPnpId = edid.pnpId; constexpr uint8_t kModelYearFlag = 0xff; @@ -99,8 +95,6 @@ DeviceProductInfo buildDeviceProductInfo(const Edid& edid) { if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) { const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress; info.relativeAddress = {address.a, address.b, address.c, address.d}; - } else { - info.relativeAddress = DeviceProductInfo::NO_RELATIVE_ADDRESS; } return info; } @@ -238,7 +232,6 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { constexpr size_t kDescriptorCount = 4; constexpr size_t kDescriptorLength = 18; - static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE); for (size_t i = 0; i < kDescriptorCount; i++) { if (view.size() < kDescriptorLength) { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index c355ebd4a6..b8000386ac 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -64,6 +64,8 @@ struct KnownHWCGenericLayerMetadata { const uint32_t id; }; +// See the comment for SurfaceFlinger::getHwComposer for the thread safety rules for accessing +// this class. class HWComposer { public: struct DeviceRequestedChanges { diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp index b986f3844a..2dfb9a9d11 100644 --- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp +++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp @@ -25,7 +25,7 @@ #include "FrameTracer.h" #include <android-base/stringprintf.h> -#include <perfetto/trace/clock_snapshot.pbzero.h> +#include <perfetto/common/builtin_clock.pbzero.h> #include <algorithm> #include <mutex> @@ -34,7 +34,6 @@ PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::FrameTracer::FrameTracerData namespace android { -using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock; void FrameTracer::initialize() { std::call_once(mInitializationFlag, [this]() { perfetto::TracingInitArgs args; @@ -136,7 +135,7 @@ void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp, FrameEvent::BufferEventType type, nsecs_t duration) { auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(Clock::MONOTONIC); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); packet->set_timestamp(timestamp); auto* event = packet->set_graphics_frame_event()->set_buffer_event(); event->set_buffer_id(static_cast<uint32_t>(bufferID)); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 13049eddf2..f36b67da8e 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2238,6 +2238,7 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, layerInfo->set_effective_scaling_mode(getEffectiveScalingMode()); layerInfo->set_corner_radius(getRoundedCornerState().radius); + layerInfo->set_background_blur_radius(getBackgroundBlurRadius()); LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform()); LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), [&]() { return layerInfo->mutable_position(); }); diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 27353d8c0b..398fd40442 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -36,6 +36,7 @@ #include "DisplayDevice.h" #include "Layer.h" +#include "Promise.h" #include "Scheduler/DispSync.h" #include "SurfaceFlinger.h" @@ -336,8 +337,20 @@ void RegionSamplingThread::captureSample() { return; } - const auto device = mFlinger.getDefaultDisplayDevice(); - const auto orientation = ui::Transform::toRotationFlags(device->getOrientation()); + wp<const DisplayDevice> displayWeak; + + ui::LayerStack layerStack; + ui::Transform::RotationFlags orientation; + ui::Size displaySize; + + { + // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread + const sp<const DisplayDevice> display = mFlinger.getDefaultDisplayDevice(); + displayWeak = display; + layerStack = display->getLayerStack(); + orientation = ui::Transform::toRotationFlags(display->getOrientation()); + displaySize = display->getSize(); + } std::vector<RegionSamplingThread::Descriptor> descriptors; Region sampleRegion; @@ -346,20 +359,18 @@ void RegionSamplingThread::captureSample() { descriptors.emplace_back(descriptor); } - const Rect sampledArea = sampleRegion.bounds(); - auto dx = 0; auto dy = 0; switch (orientation) { case ui::Transform::ROT_90: - dx = device->getWidth(); + dx = displaySize.getWidth(); break; case ui::Transform::ROT_180: - dx = device->getWidth(); - dy = device->getHeight(); + dx = displaySize.getWidth(); + dy = displaySize.getHeight(); break; case ui::Transform::ROT_270: - dy = device->getHeight(); + dy = displaySize.getHeight(); break; default: break; @@ -368,8 +379,13 @@ void RegionSamplingThread::captureSample() { ui::Transform t(orientation); auto screencapRegion = t.transform(sampleRegion); screencapRegion = screencapRegion.translate(dx, dy); - DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(), - sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation); + + const Rect sampledBounds = sampleRegion.bounds(); + + SurfaceFlinger::RenderAreaFuture renderAreaFuture = promise::defer([=] { + return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(), + ui::Dataspace::V0_SRGB, orientation); + }); std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners; @@ -393,9 +409,9 @@ void RegionSamplingThread::captureSample() { constexpr bool roundOutwards = true; Rect transformed = transform.transform(bounds, roundOutwards); - // If this layer doesn't intersect with the larger sampledArea, skip capturing it + // If this layer doesn't intersect with the larger sampledBounds, skip capturing it Rect ignore; - if (!transformed.intersect(sampledArea, &ignore)) return; + if (!transformed.intersect(sampledBounds, &ignore)) return; // If the layer doesn't intersect a sampling area, skip capturing it bool intersectsAnyArea = false; @@ -411,22 +427,22 @@ void RegionSamplingThread::captureSample() { bounds.top, bounds.right, bounds.bottom); visitor(layer); }; - mFlinger.traverseLayersInDisplay(device, filterVisitor); + mFlinger.traverseLayersInLayerStack(layerStack, filterVisitor); }; sp<GraphicBuffer> buffer = nullptr; - if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() && - mCachedBuffer->getHeight() == sampledArea.getHeight()) { + if (mCachedBuffer && mCachedBuffer->getWidth() == sampledBounds.getWidth() && + mCachedBuffer->getHeight() == sampledBounds.getHeight()) { buffer = mCachedBuffer; } else { const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; - buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(), + buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(), PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread"); } bool ignored; - mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */, - true /* regionSampling */, ignored); + mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, + false /* identityTransform */, true /* regionSampling */, ignored); std::vector<Descriptor> activeDescriptors; for (const auto& descriptor : descriptors) { @@ -437,7 +453,7 @@ void RegionSamplingThread::captureSample() { ALOGV("Sampling %zu descriptors", activeDescriptors.size()); std::vector<float> lumas = - sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation); + sampleBuffer(buffer, sampledBounds.leftTop(), activeDescriptors, orientation); if (lumas.size() != activeDescriptors.size()) { ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(), activeDescriptors.size()); diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index 6b0455ae87..a6246d9a6a 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -23,11 +23,9 @@ public: static float getCaptureFillValue(CaptureFill captureFill); - RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill, - ui::Dataspace reqDataSpace, const Rect& displayViewport, - RotationFlags rotation = ui::Transform::ROT_0) - : mReqWidth(reqWidth), - mReqHeight(reqHeight), + RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace, + const Rect& displayViewport, RotationFlags rotation = ui::Transform::ROT_0) + : mReqSize(reqSize), mReqDataSpace(reqDataSpace), mCaptureFill(captureFill), mRotationFlags(rotation), @@ -70,8 +68,8 @@ public: RotationFlags getRotationFlags() const { return mRotationFlags; } // Returns the size of the physical render area. - int getReqWidth() const { return static_cast<int>(mReqWidth); } - int getReqHeight() const { return static_cast<int>(mReqHeight); } + int getReqWidth() const { return mReqSize.width; } + int getReqHeight() const { return mReqSize.height; } // Returns the composition data space of the render area. ui::Dataspace getReqDataSpace() const { return mReqDataSpace; } @@ -86,8 +84,7 @@ public: const Rect& getDisplayViewport() const { return mDisplayViewport; } private: - const uint32_t mReqWidth; - const uint32_t mReqHeight; + const ui::Size mReqSize; const ui::Dataspace mReqDataSpace; const CaptureFill mCaptureFill; const RotationFlags mRotationFlags; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 2e019039f8..e68c48407b 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -28,7 +28,7 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> -#include <android/hardware/power/1.0/IPower.h> +#include <android/hardware/power/Boost.h> #include <android/native_window.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -148,7 +148,7 @@ using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; using namespace android::sysprop; -using android::hardware::power::V1_0::PowerHint; +using android::hardware::power::Boost; using base::StringAppendF; using ui::ColorMode; using ui::Dataspace; @@ -423,6 +423,10 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS; auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize)); mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize; + mGraphicBufferProducerListSizeLogThreshold = + std::max(static_cast<int>(0.95 * + static_cast<double>(mMaxGraphicBufferProducerListSize)), + 1); property_get("debug.sf.luma_sampling", value, "1"); mLumaSampling = atoi(value); @@ -871,7 +875,7 @@ status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, Display info->density /= ACONFIGURATION_DENSITY_MEDIUM; info->secure = display->isSecure(); - info->deviceProductInfo = getDeviceProductInfoLocked(*display); + info->deviceProductInfo = display->getDeviceProductInfo(); return NO_ERROR; } @@ -1338,30 +1342,6 @@ status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken, return NO_ERROR; } -std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked( - const DisplayDevice& display) const { - // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to - // avoid repetitive HAL IPC and EDID parsing. - const auto displayId = display.getId(); - LOG_FATAL_IF(!displayId); - - const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId); - LOG_FATAL_IF(!hwcDisplayId); - - uint8_t port; - DisplayIdentificationData data; - if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) { - ALOGV("%s: No identification data.", __FUNCTION__); - return {}; - } - - const auto info = parseDisplayIdentificationData(port, data); - if (!info) { - return {}; - } - return info->deviceProductInfo; -} - status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, @@ -1529,10 +1509,10 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, f .get(); } -status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) { - PowerHint powerHint = static_cast<PowerHint>(hintId); +status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) { + Boost powerBoost = static_cast<Boost>(boostId); - if (powerHint == PowerHint::INTERACTION) { + if (powerBoost == Boost::INTERACTION) { mScheduler->notifyTouchEvent(); } @@ -2446,8 +2426,10 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { } DisplayDeviceState state; - state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId), - event.hwcDisplayId}; + state.physical = {.id = displayId, + .type = getHwComposer().getDisplayConnectionType(displayId), + .hwcDisplayId = event.hwcDisplayId, + .deviceProductInfo = info->deviceProductInfo}; state.isSecure = true; // All physical displays are currently considered secure. state.displayName = info->name; @@ -2563,6 +2545,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( LOG_ALWAYS_FATAL_IF(!displayId); auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId)); display->setActiveConfig(activeConfigId); + display->setDeviceProductInfo(state.physical->deviceProductInfo); } display->setLayerStack(state.layerStack); @@ -3206,6 +3189,11 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers", mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize, mNumLayers.load()); + if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) { + ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers", + mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize, + mNumLayers.load()); + } } if (const auto display = getDefaultDisplayDeviceLocked()) { @@ -4731,7 +4719,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co StringAppendF(&result, "Composition layers\n"); mDrawingState.traverseInZOrder([&](Layer* layer) { auto* compositionState = layer->getCompositionState(); - if (!compositionState) return; + if (!compositionState || !compositionState->isVisible) return; android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer, layer->getDebugName() ? layer->getDebugName() @@ -4895,7 +4883,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: case GET_DISPLAYED_CONTENT_SAMPLE: - case NOTIFY_POWER_HINT: + case NOTIFY_POWER_BOOST: case SET_GLOBAL_SHADOW_SETTINGS: case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: { // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the @@ -5427,27 +5415,33 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken, renderAreaRotation = ui::Transform::ROT_0; } - sp<DisplayDevice> display; + wp<DisplayDevice> displayWeak; + ui::LayerStack layerStack; + ui::Size reqSize(reqWidth, reqHeight); { Mutex::Autolock lock(mStateLock); - - display = getDisplayDeviceLocked(displayToken); + sp<DisplayDevice> display = getDisplayDeviceLocked(displayToken); if (!display) return NAME_NOT_FOUND; + displayWeak = display; + layerStack = display->getLayerStack(); // set the requested width/height to the logical display viewport size // by default if (reqWidth == 0 || reqHeight == 0) { - reqWidth = uint32_t(display->getViewport().width()); - reqHeight = uint32_t(display->getViewport().height()); + reqSize = display->getViewport().getSize(); } } - DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace, - renderAreaRotation, captureSecureLayers); - auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display, - std::placeholders::_1); - return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, - useIdentityTransform, outCapturedSecureLayers); + RenderAreaFuture renderAreaFuture = promise::defer([=] { + return DisplayRenderArea::create(displayWeak, sourceCrop, reqSize, reqDataspace, + renderAreaRotation, captureSecureLayers); + }); + + auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) { + traverseLayersInLayerStack(layerStack, visitor); + }; + return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, outBuffer, + reqPixelFormat, useIdentityTransform, outCapturedSecureLayers); } static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) { @@ -5503,19 +5497,20 @@ sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) { status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace, sp<GraphicBuffer>* outBuffer) { - sp<DisplayDevice> display; - uint32_t width; - uint32_t height; + ui::LayerStack layerStack; + wp<DisplayDevice> displayWeak; + ui::Size size; ui::Transform::RotationFlags captureOrientation; { Mutex::Autolock lock(mStateLock); - display = getDisplayByIdOrLayerStack(displayOrLayerStack); + sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack); if (!display) { return NAME_NOT_FOUND; } + layerStack = display->getLayerStack(); + displayWeak = display; - width = uint32_t(display->getViewport().width()); - height = uint32_t(display->getViewport().height()); + size = display->getViewport().getSize(); const auto orientation = display->getOrientation(); captureOrientation = ui::Transform::toRotationFlags(orientation); @@ -5541,14 +5536,19 @@ status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode); } - DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation, - false /* captureSecureLayers */); + RenderAreaFuture renderAreaFuture = promise::defer([=] { + return DisplayRenderArea::create(displayWeak, Rect(), size, *outDataspace, + captureOrientation, false /* captureSecureLayers */); + }); + + auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) { + traverseLayersInLayerStack(layerStack, visitor); + }; - auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display, - std::placeholders::_1); bool ignored = false; - return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888, - false /* useIdentityTransform */, + + return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size, outBuffer, + ui::PixelFormat::RGBA_8888, false /* useIdentityTransform */, ignored /* outCapturedSecureLayers */); } @@ -5562,9 +5562,9 @@ status_t SurfaceFlinger::captureLayers( class LayerRenderArea : public RenderArea { public: LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop, - int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace, - bool childrenOnly, const Rect& displayViewport) - : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport), + ui::Size reqSize, Dataspace reqDataSpace, bool childrenOnly, + const Rect& displayViewport) + : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, displayViewport), mLayer(layer), mCrop(crop), mNeedsFiltering(false), @@ -5639,8 +5639,7 @@ status_t SurfaceFlinger::captureLayers( const bool mChildrenOnly; }; - int reqWidth = 0; - int reqHeight = 0; + ui::Size reqSize; sp<Layer> parent; Rect crop(sourceCrop); std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers; @@ -5677,8 +5676,7 @@ status_t SurfaceFlinger::captureLayers( // crop was not specified, or an invalid frame scale was provided. return BAD_VALUE; } - reqWidth = crop.width() * frameScale; - reqHeight = crop.height() * frameScale; + reqSize = ui::Size(crop.width() * frameScale, crop.height() * frameScale); for (const auto& handle : excludeHandles) { sp<Layer> excludeLayer = fromHandleLocked(handle).promote(); @@ -5699,15 +5697,18 @@ status_t SurfaceFlinger::captureLayers( } // mStateLock // really small crop or frameScale - if (reqWidth <= 0) { - reqWidth = 1; + if (reqSize.width <= 0) { + reqSize.width = 1; } - if (reqHeight <= 0) { - reqHeight = 1; + if (reqSize.height <= 0) { + reqSize.height = 1; } - LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly, - displayViewport); + RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> { + return std::make_unique<LayerRenderArea>(this, parent, crop, reqSize, reqDataspace, + childrenOnly, displayViewport); + }); + auto traverseLayers = [parent, childrenOnly, &excludeLayers](const LayerVector::Visitor& visitor) { parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { @@ -5730,14 +5731,14 @@ status_t SurfaceFlinger::captureLayers( }; bool outCapturedSecureLayers = false; - return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false, - outCapturedSecureLayers); + return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, outBuffer, + reqPixelFormat, false, outCapturedSecureLayers); } -status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, +status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers, - sp<GraphicBuffer>* outBuffer, - const ui::PixelFormat reqPixelFormat, + ui::Size bufferSize, sp<GraphicBuffer>* outBuffer, + ui::PixelFormat reqPixelFormat, bool useIdentityTransform, bool& outCapturedSecureLayers) { ATRACE_CALL(); @@ -5745,16 +5746,16 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, // TODO(b/116112787) Make buffer usage a parameter. const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; - *outBuffer = - getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(), - static_cast<android_pixel_format>(reqPixelFormat), 1, - usage, "screenshot"); + *outBuffer = getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), + static_cast<android_pixel_format>(reqPixelFormat), + 1, usage, "screenshot"); - return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform, - false /* regionSampling */, outCapturedSecureLayers); + return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, *outBuffer, + useIdentityTransform, false /* regionSampling */, + outCapturedSecureLayers); } -status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, +status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers, const sp<GraphicBuffer>& buffer, bool useIdentityTransform, bool regionSampling, @@ -5767,23 +5768,28 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, do { std::tie(result, syncFd) = - schedule([&] { + schedule([&]() -> std::pair<status_t, int> { if (mRefreshPending) { - ATRACE_NAME("Skipping screenshot for now"); - return std::make_pair(EAGAIN, -1); + ALOGW("Skipping screenshot for now"); + return {EAGAIN, -1}; + } + std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get(); + if (!renderArea) { + ALOGW("Skipping screen capture because of invalid render area."); + return {NO_MEMORY, -1}; } status_t result = NO_ERROR; int fd = -1; Mutex::Autolock lock(mStateLock); - renderArea.render([&] { - result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(), + renderArea->render([&] { + result = captureScreenImplLocked(*renderArea, traverseLayers, buffer.get(), useIdentityTransform, forSystem, &fd, regionSampling, outCapturedSecureLayers); }); - return std::make_pair(result, fd); + return {result, fd}; }).get(); } while (result == EAGAIN); @@ -5797,8 +5803,9 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, bool useIdentityTransform, - bool regionSampling, int* outSyncFd) { + const sp<GraphicBuffer>& buffer, + bool useIdentityTransform, bool regionSampling, + int* outSyncFd) { ATRACE_CALL(); const auto reqWidth = renderArea.getReqWidth(); @@ -5896,7 +5903,7 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, + const sp<GraphicBuffer>& buffer, bool useIdentityTransform, bool forSystem, int* outSyncFd, bool regionSampling, bool& outCapturedSecureLayers) { @@ -5941,17 +5948,17 @@ void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& layersSortedByZ.traverseInReverseZOrder(stateSet, visitor); } -void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display, - const LayerVector::Visitor& visitor) { +void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, + const LayerVector::Visitor& visitor) { // We loop through the first level of layers without traversing, // as we need to determine which layers belong to the requested display. for (const auto& layer : mDrawingState.layersSortedByZ) { - if (!layer->belongsToDisplay(display->getLayerStack(), false)) { + if (!layer->belongsToDisplay(layerStack, false)) { continue; } // relative layers are traversed in Layer::traverseInZOrder layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { - if (!layer->belongsToDisplay(display->getLayerStack(), false)) { + if (!layer->belongsToDisplay(layerStack, false)) { return; } if (!layer->isVisible()) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index ccaeb2d858..6a3937f577 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -498,7 +498,7 @@ private: status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) const override; status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override; - status_t notifyPowerHint(int32_t hintId) override; + status_t notifyPowerBoost(int32_t boostId) override; status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, float lightPosY, float lightPosZ, float lightRadius) override; status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, @@ -718,25 +718,25 @@ private: void startBootAnim(); using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>; + using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>; void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, bool useIdentityTransform, + const sp<GraphicBuffer>& buffer, bool useIdentityTransform, bool regionSampling, int* outSyncFd); - status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, - sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat, + status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize, + sp<GraphicBuffer>* outBuffer, ui::PixelFormat, bool useIdentityTransform, bool& outCapturedSecureLayers); - status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, - const sp<GraphicBuffer>& buffer, bool useIdentityTransform, - bool regionSampling, bool& outCapturedSecureLayers); + status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, const sp<GraphicBuffer>&, + bool useIdentityTransform, bool regionSampling, + bool& outCapturedSecureLayers); sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock); sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock); status_t captureScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, bool useIdentityTransform, + const sp<GraphicBuffer>& buffer, bool useIdentityTransform, bool forSystem, int* outSyncFd, bool regionSampling, bool& outCapturedSecureLayers); - void traverseLayersInDisplay(const sp<const DisplayDevice>& display, - const LayerVector::Visitor& visitor); + void traverseLayersInLayerStack(ui::LayerStack, const LayerVector::Visitor&); sp<StartPropertySetThread> mStartPropertySetThread; @@ -783,8 +783,6 @@ private: return getDefaultDisplayDeviceLocked(); } - std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const; - // mark a region of a layer stack dirty. this updates the dirty // region of all screens presenting this layer stack. void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty); @@ -1017,6 +1015,10 @@ private: // Can't be unordered_set because wp<> isn't hashable std::set<wp<IBinder>> mGraphicBufferProducerList; size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS; + // If there are more GraphicBufferProducers tracked by SurfaceFlinger than + // this threshold, then begin logging. + size_t mGraphicBufferProducerListSizeLogThreshold = + static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS)); void removeGraphicBufferProducerAsync(const wp<IBinder>&); diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 32d722e9c9..456891e11f 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -233,23 +233,22 @@ void CompositionTest::captureScreenComposition() { constexpr bool forSystem = true; constexpr bool regionSampling = false; - DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB, - ui::Transform::ROT_0); + auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(), + ui::Dataspace::V0_SRGB, ui::Transform::ROT_0); auto traverseLayers = [this](const LayerVector::Visitor& visitor) { - return mFlinger.traverseLayersInDisplay(mDisplay, visitor); + return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(), visitor); }; // TODO: Eliminate expensive/real allocation if possible. const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; - mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(), + mCaptureScreenBuffer = new GraphicBuffer(renderArea->getReqWidth(), renderArea->getReqHeight(), HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot"); int fd = -1; status_t result = - mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(), + mFlinger.captureScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer.get(), useIdentityTransform, forSystem, &fd, regionSampling); if (fd >= 0) { close(fd); @@ -348,7 +347,7 @@ struct BaseDisplayVariant { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>&, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -397,7 +396,7 @@ struct BaseDisplayVariant { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>&, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -648,7 +647,7 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>& layerSettings, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -697,7 +696,7 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>& layerSettings, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -775,7 +774,7 @@ struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>& layerSettings, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp index 2a0e913e7a..cc6a60ce27 100644 --- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp @@ -22,6 +22,8 @@ #include "DisplayHardware/DisplayIdentification.h" +using ::testing::ElementsAre; + namespace android { namespace { @@ -312,86 +314,85 @@ TEST(DisplayIdentificationTest, deviceProductInfo) { using ManufactureYear = DeviceProductInfo::ManufactureYear; using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear; using ModelYear = DeviceProductInfo::ModelYear; - using RelativeAddress = DeviceProductInfo::RelativeAddress; { const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("", info.name.data()); + EXPECT_EQ("", info.name); EXPECT_STREQ("SEC", info.manufacturerPnpId.data()); - EXPECT_STREQ("12610", info.productId.data()); + EXPECT_EQ("12610", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate)); EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year); - EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress); + EXPECT_TRUE(info.relativeAddress.empty()); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("HP ZR30w", info.name.data()); + EXPECT_EQ("HP ZR30w", info.name); EXPECT_STREQ("HWP", info.manufacturerPnpId.data()); - EXPECT_STREQ("10348", info.productId.data()); + EXPECT_EQ("10348", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate)); const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate); EXPECT_EQ(2012, date.year); EXPECT_EQ(2, date.week); - EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress); + EXPECT_TRUE(info.relativeAddress.empty()); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("SAMSUNG", info.name.data()); + EXPECT_EQ("SAMSUNG", info.name); EXPECT_STREQ("SAM", info.manufacturerPnpId.data()); - EXPECT_STREQ("2302", info.productId.data()); + EXPECT_EQ("2302", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate)); const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate); EXPECT_EQ(2011, date.year); EXPECT_EQ(41, date.week); - EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress); + EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0)); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("Panasonic-TV", info.name.data()); + EXPECT_EQ("Panasonic-TV", info.name); EXPECT_STREQ("MEI", info.manufacturerPnpId.data()); - EXPECT_STREQ("41622", info.productId.data()); + EXPECT_EQ("41622", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate)); const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate); EXPECT_EQ(2019, date.year); - EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress); + EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0)); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("Hisense", info.name.data()); + EXPECT_EQ("Hisense", info.name); EXPECT_STREQ("HEC", info.manufacturerPnpId.data()); - EXPECT_STREQ("0", info.productId.data()); + EXPECT_EQ("0", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate)); const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate); EXPECT_EQ(2019, date.year); EXPECT_EQ(18, date.week); - EXPECT_EQ((RelativeAddress{{1, 2, 3, 4}}), info.relativeAddress); + EXPECT_THAT(info.relativeAddress, ElementsAre(1, 2, 3, 4)); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("LP2361", info.name.data()); + EXPECT_EQ("LP2361", info.name); EXPECT_STREQ("CTL", info.manufacturerPnpId.data()); - EXPECT_STREQ("9373", info.productId.data()); + EXPECT_EQ("9373", info.productId); ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate)); EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year); - EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress); + EXPECT_TRUE(info.relativeAddress.empty()); } } diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index ce5f35cdf3..9130b04499 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -23,6 +23,7 @@ #include <type_traits> +#include <android/hardware/power/Boost.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> #include <compositionengine/impl/Display.h> @@ -57,6 +58,8 @@ namespace { namespace hal = android::hardware::graphics::composer::hal; +using android::hardware::power::Boost; + using testing::_; using testing::AnyNumber; using testing::DoAll; @@ -1347,6 +1350,30 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { } /* ------------------------------------------------------------------------ + * SurfaceFlinger::notifyPowerBoost + */ + +TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) { + mFlinger.scheduler()->replaceTouchTimer(100); + std::this_thread::sleep_for(10ms); // wait for callback to be triggered + EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch + + std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + + EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT))); + std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + + std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + + EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION))); + std::this_thread::sleep_for(10ms); // wait for callback to be triggered. + EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); +} + +/* ------------------------------------------------------------------------ * DisplayDevice::GetBestColorMode */ class GetBestColorModeTest : public DisplayTransactionTest { @@ -1794,7 +1821,7 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { ASSERT_TRUE(displayId); const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; ASSERT_TRUE(hwcDisplayId); - state.physical = {*displayId, *connectionType, *hwcDisplayId}; + state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId}; } state.isSecure = static_cast<bool>(Case::Display::SECURE); @@ -1970,7 +1997,9 @@ void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& di ASSERT_TRUE(displayId); const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; ASSERT_TRUE(hwcDisplayId); - expectedPhysical = {*displayId, *connectionType, *hwcDisplayId}; + expectedPhysical = {.id = *displayId, + .type = *connectionType, + .hwcDisplayId = *hwcDisplayId}; } // The display should have been set up in the current display state diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index d5ecae81cd..b7067a6f24 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -72,13 +72,31 @@ public: auto& mutableEventControlThread() { return mEventControlThread; } auto& mutablePrimaryDispSync() { return mPrimaryDispSync; } auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; } + auto mutableLayerHistory() { return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get()); } + auto mutableLayerHistoryV2() { return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get()); } + void replaceTouchTimer(int64_t millis) { + if (mTouchTimer) { + mTouchTimer.reset(); + } + mTouchTimer.emplace( + std::chrono::milliseconds(millis), + [this] { touchTimerCallback(TimerState::Reset); }, + [this] { touchTimerCallback(TimerState::Expired); }); + mTouchTimer->start(); + } + + bool isTouchActive() { + std::lock_guard<std::mutex> lock(mFeatureStateLock); + return mFeatures.touch == Scheduler::TouchState::Active; + } + ~TestableScheduler() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the Scheduler instance may diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 38bc8a19cf..f630103cb6 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -321,6 +321,8 @@ public: return mFlinger->onInitializeDisplays(); } + auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); } + // Allow reading display state without locking, as if called on the SF main thread. auto setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS { @@ -331,7 +333,7 @@ public: auto captureScreenImplLocked(const RenderArea& renderArea, SurfaceFlinger::TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, bool useIdentityTransform, + const sp<GraphicBuffer>& buffer, bool useIdentityTransform, bool forSystem, int* outSyncFd, bool regionSampling) { bool ignored; return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer, @@ -339,9 +341,9 @@ public: regionSampling, ignored); } - auto traverseLayersInDisplay(const sp<const DisplayDevice>& display, - const LayerVector::Visitor& visitor) { - return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor); + auto traverseLayersInLayerStack(ui::LayerStack layerStack, + const LayerVector::Visitor& visitor) { + return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, visitor); } auto getDisplayNativePrimaries(const sp<IBinder>& displayToken, @@ -633,7 +635,7 @@ public: if (const auto type = mCreationArgs.connectionType) { LOG_ALWAYS_FATAL_IF(!displayId); LOG_ALWAYS_FATAL_IF(!mHwcDisplayId); - state.physical = {*displayId, *type, *mHwcDisplayId}; + state.physical = {.id = *displayId, .type = *type, .hwcDisplayId = *mHwcDisplayId}; } state.isSecure = mCreationArgs.isSecure; diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp new file mode 100644 index 0000000000..e3c254d82a --- /dev/null +++ b/services/vibratorservice/Android.bp @@ -0,0 +1,53 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_shared { + name: "libvibratorservice", + + srcs: [ + "VibratorCallbackScheduler.cpp", + "VibratorHalWrapper.cpp", + ], + + aidl: { + local_include_dirs: ["include"], + include_dirs: [ + "hardware/interfaces/vibrator/aidl/android/hardware/vibrator", + ], + export_aidl_headers: true + }, + + shared_libs: [ + "libbinder", + "libhidlbase", + "liblog", + "libutils", + "android.hardware.vibrator-cpp", + "android.hardware.vibrator@1.0", + "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", + "android.hardware.vibrator@1.3", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], + + local_include_dirs: ["include"], + + export_include_dirs: ["include"], +} diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp new file mode 100644 index 0000000000..3f8cd673ff --- /dev/null +++ b/services/vibratorservice/VibratorCallbackScheduler.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <chrono> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +bool DelayedCallback::isExpired() const { + return mExpiration <= std::chrono::steady_clock::now(); +} + +DelayedCallback::Timestamp DelayedCallback::getExpiration() const { + return mExpiration; +} + +void DelayedCallback::run() const { + mCallback(); +} + +bool DelayedCallback::operator<(const DelayedCallback& other) const { + return mExpiration < other.mExpiration; +} + +bool DelayedCallback::operator>(const DelayedCallback& other) const { + return mExpiration > other.mExpiration; +} + +// ------------------------------------------------------------------------------------------------- + +CallbackScheduler::~CallbackScheduler() { + { + std::lock_guard<std::mutex> lock(mMutex); + mFinished = true; + } + mCondition.notify_all(); + if (mCallbackThread && mCallbackThread->joinable()) { + mCallbackThread->join(); + } +} + +void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) { + { + std::lock_guard<std::mutex> lock(mMutex); + if (mCallbackThread == nullptr) { + mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this); + } + mQueue.emplace(DelayedCallback(callback, delay)); + } + mCondition.notify_all(); +} + +void CallbackScheduler::loop() { + while (true) { + std::lock_guard<std::mutex> lock(mMutex); + if (mFinished) { + // Destructor was called, so let the callback thread die. + break; + } + while (!mQueue.empty() && mQueue.top().isExpired()) { + mQueue.top().run(); + mQueue.pop(); + } + if (mQueue.empty()) { + // Wait until a new callback is scheduled. + mCondition.wait(mMutex); + } else { + // Wait until next callback expires, or a new one is scheduled. + mCondition.wait_until(mMutex, mQueue.top().getExpiration()); + } + } +} + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp new file mode 100644 index 0000000000..1420bf5604 --- /dev/null +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapper" + +#include <android/hardware/vibrator/1.3/IVibrator.h> +#include <android/hardware/vibrator/BnVibratorCallback.h> +#include <android/hardware/vibrator/IVibrator.h> +#include <hardware/vibrator.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using std::chrono::milliseconds; + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; +namespace V1_2 = android::hardware::vibrator::V1_2; +namespace V1_3 = android::hardware::vibrator::V1_3; +namespace Aidl = android::hardware::vibrator; + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +template <class T> +HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) { + if (cache.has_value()) { + // Return copy of cached value. + return HalResult<T>::ok(*cache); + } + HalResult<T> ret = loadFn(); + if (ret.isOk()) { + // Cache copy of returned value. + cache.emplace(ret.value()); + } + return ret; +} + +template <class T> +bool isStaticCastValid(Effect effect) { + T castEffect = static_cast<T>(effect); + auto iter = hardware::hidl_enum_range<T>(); + return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end()); +} + +// ------------------------------------------------------------------------------------------------- + +template <typename T> +HalResult<T> HalResult<T>::ok(T value) { + return HalResult(value); +} + +template <typename T> +HalResult<T> HalResult<T>::failed() { + return HalResult(/* unsupported= */ false); +} + +template <typename T> +HalResult<T> HalResult<T>::unsupported() { + return HalResult(/* unsupported= */ true); +} + +template <typename T> +HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) { + if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult<T>::unsupported(); + } + if (status.isOk()) { + return HalResult<T>::ok(data); + } + return HalResult<T>::failed(); +} + +template <typename T> +HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) { + switch (status) { + case V1_0::Status::OK: + return HalResult<T>::ok(data); + case V1_0::Status::UNSUPPORTED_OPERATION: + return HalResult<T>::unsupported(); + default: + return HalResult<T>::failed(); + } +} + +template <typename T> +template <typename R> +HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) { + return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(); +} + +template <typename T> +template <typename R> +HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) { + return ret.isOk() ? HalResult<T>::fromStatus(status, data) : HalResult<T>::failed(); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> HalResult<void>::ok() { + return HalResult(); +} + +HalResult<void> HalResult<void>::failed() { + return HalResult(/* failed= */ true); +} + +HalResult<void> HalResult<void>::unsupported() { + return HalResult(/* failed= */ false, /* unsupported= */ true); +} + +HalResult<void> HalResult<void>::fromStatus(binder::Status status) { + if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult<void>::unsupported(); + } + if (status.isOk()) { + return HalResult<void>::ok(); + } + return HalResult<void>::failed(); +} + +HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) { + switch (status) { + case V1_0::Status::OK: + return HalResult<void>::ok(); + case V1_0::Status::UNSUPPORTED_OPERATION: + return HalResult<void>::unsupported(); + default: + return HalResult<void>::failed(); + } +} + +template <typename R> +HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) { + return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(); +} + +// ------------------------------------------------------------------------------------------------- + +class HalCallbackWrapper : public Aidl::BnVibratorCallback { +public: + HalCallbackWrapper(std::function<void()> completionCallback) + : mCompletionCallback(completionCallback) {} + + binder::Status onComplete() override { + mCompletionCallback(); + return binder::Status::ok(); + } + +private: + const std::function<void()> mCompletionCallback; +}; + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> AidlHalWrapper::ping() { + return IInterface::asBinder(mHandle)->pingBinder() ? HalResult<void>::ok() + : HalResult<void>::failed(); +} + +HalResult<void> AidlHalWrapper::on(milliseconds timeout, + const std::function<void()>& completionCallback) { + HalResult<Capabilities> capabilities = getCapabilities(); + bool supportsCallback = capabilities.isOk() && + static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK); + auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; + + auto ret = HalResult<void>::fromStatus(mHandle->on(timeout.count(), cb)); + if (!supportsCallback && ret.isOk()) { + mCallbackScheduler->schedule(completionCallback, timeout); + } + + return ret; +} + +HalResult<void> AidlHalWrapper::off() { + return HalResult<void>::fromStatus(mHandle->off()); +} + +HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) { + float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max(); + return HalResult<void>::fromStatus(mHandle->setAmplitude(convertedAmplitude)); +} + +HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) { + return HalResult<void>::fromStatus(mHandle->setExternalControl(enabled)); +} + +HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) { + return HalResult<void>::fromStatus(mHandle->alwaysOnEnable(id, effect, strength)); +} + +HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) { + return HalResult<void>::fromStatus(mHandle->alwaysOnDisable(id)); +} + +HalResult<Capabilities> AidlHalWrapper::getCapabilities() { + std::lock_guard<std::mutex> lock(mCapabilitiesMutex); + return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this), + mCapabilities); +} + +HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() { + std::lock_guard<std::mutex> lock(mSupportedEffectsMutex); + return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal, + this), + mSupportedEffects); +} + +HalResult<milliseconds> AidlHalWrapper::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + HalResult<Capabilities> capabilities = getCapabilities(); + bool supportsCallback = capabilities.isOk() && + static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK); + auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; + + int32_t lengthMs; + auto result = mHandle->perform(effect, strength, cb, &lengthMs); + milliseconds length = milliseconds(lengthMs); + + auto ret = HalResult<milliseconds>::fromStatus(result, length); + if (!supportsCallback && ret.isOk()) { + mCallbackScheduler->schedule(completionCallback, length); + } + + return ret; +} + +HalResult<void> AidlHalWrapper::performComposedEffect( + const std::vector<CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) { + // This method should always support callbacks, so no need to double check. + auto cb = new HalCallbackWrapper(completionCallback); + return HalResult<void>::fromStatus(mHandle->compose(primitiveEffects, cb)); +} + +HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() { + int32_t capabilities = 0; + auto result = mHandle->getCapabilities(&capabilities); + return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities)); +} + +HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() { + std::vector<Effect> supportedEffects; + auto result = mHandle->getSupportedEffects(&supportedEffects); + return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> HidlHalWrapperV1_0::ping() { + auto result = mHandleV1_0->ping(); + return HalResult<void>::fromReturn(result); +} + +HalResult<void> HidlHalWrapperV1_0::on(milliseconds timeout, + const std::function<void()>& completionCallback) { + auto result = mHandleV1_0->on(timeout.count()); + auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + if (ret.isOk()) { + mCallbackScheduler->schedule(completionCallback, timeout); + } + return ret; +} + +HalResult<void> HidlHalWrapperV1_0::off() { + auto result = mHandleV1_0->off(); + return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); +} + +HalResult<void> HidlHalWrapperV1_0::setAmplitude(int32_t amplitude) { + auto result = mHandleV1_0->setAmplitude(static_cast<uint8_t>(amplitude)); + return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); +} + +HalResult<void> HidlHalWrapperV1_0::setExternalControl(bool) { + ALOGV("Skipped setExternalControl because Vibrator HAL does not support it"); + return HalResult<void>::unsupported(); +} + +HalResult<void> HidlHalWrapperV1_0::alwaysOnEnable(int32_t, Effect, EffectStrength) { + ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available"); + return HalResult<void>::unsupported(); +} + +HalResult<void> HidlHalWrapperV1_0::alwaysOnDisable(int32_t) { + ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available"); + return HalResult<void>::unsupported(); +} + +HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilities() { + std::lock_guard<std::mutex> lock(mCapabilitiesMutex); + return loadCached<Capabilities>(std::bind(&HidlHalWrapperV1_0::getCapabilitiesInternal, this), + mCapabilities); +} + +HalResult<std::vector<Effect>> HidlHalWrapperV1_0::getSupportedEffects() { + ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available"); + return HalResult<std::vector<Effect>>::unsupported(); +} + +HalResult<milliseconds> HidlHalWrapperV1_0::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + if (isStaticCastValid<V1_0::Effect>(effect)) { + return performInternalV1_0(effect, strength, completionCallback); + } + + ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", + Aidl::toString(effect).c_str()); + return HalResult<milliseconds>::unsupported(); +} + +HalResult<void> HidlHalWrapperV1_0::performComposedEffect(const std::vector<CompositeEffect>&, + const std::function<void()>&) { + ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available"); + return HalResult<void>::unsupported(); +} + +HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilitiesInternal() { + hardware::Return<bool> result = mHandleV1_0->supportsAmplitudeControl(); + Capabilities capabilities = + result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE; + return HalResult<Capabilities>::fromReturn(result, capabilities); +} + +template <class I, class T> +HalResult<milliseconds> HidlHalWrapperV1_0::performInternal( + perform_fn<I, T> performFn, sp<I> handle, T effect, EffectStrength strength, + const std::function<void()>& completionCallback) { + V1_0::Status status; + int32_t lengthMs; + auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) { + status = retStatus; + lengthMs = retLengthMs; + }; + + V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength); + auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback); + milliseconds length = milliseconds(lengthMs); + + auto ret = HalResult<milliseconds>::fromReturn(result, status, length); + if (ret.isOk()) { + mCallbackScheduler->schedule(completionCallback, length); + } + + return ret; +} + +HalResult<milliseconds> HidlHalWrapperV1_0::performInternalV1_0( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + V1_0::Effect e = static_cast<V1_0::Effect>(effect); + return performInternal(&V1_0::IVibrator::perform, mHandleV1_0, e, strength, completionCallback); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<milliseconds> HidlHalWrapperV1_1::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + if (isStaticCastValid<V1_0::Effect>(effect)) { + return performInternalV1_0(effect, strength, completionCallback); + } + if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { + return performInternalV1_1(effect, strength, completionCallback); + } + + ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", + Aidl::toString(effect).c_str()); + return HalResult<milliseconds>::unsupported(); +} + +HalResult<milliseconds> HidlHalWrapperV1_1::performInternalV1_1( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect); + return performInternal(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength, + completionCallback); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<milliseconds> HidlHalWrapperV1_2::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + if (isStaticCastValid<V1_0::Effect>(effect)) { + return performInternalV1_0(effect, strength, completionCallback); + } + if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { + return performInternalV1_1(effect, strength, completionCallback); + } + if (isStaticCastValid<V1_2::Effect>(effect)) { + return performInternalV1_2(effect, strength, completionCallback); + } + + ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", + Aidl::toString(effect).c_str()); + return HalResult<milliseconds>::unsupported(); +} + +HalResult<milliseconds> HidlHalWrapperV1_2::performInternalV1_2( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + V1_2::Effect e = static_cast<V1_2::Effect>(effect); + return performInternal(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength, + completionCallback); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) { + auto result = mHandleV1_3->setExternalControl(static_cast<uint32_t>(enabled)); + return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); +} + +HalResult<milliseconds> HidlHalWrapperV1_3::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + if (isStaticCastValid<V1_0::Effect>(effect)) { + return performInternalV1_0(effect, strength, completionCallback); + } + if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { + return performInternalV1_1(effect, strength, completionCallback); + } + if (isStaticCastValid<V1_2::Effect>(effect)) { + return performInternalV1_2(effect, strength, completionCallback); + } + if (isStaticCastValid<V1_3::Effect>(effect)) { + return performInternalV1_3(effect, strength, completionCallback); + } + + ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", + Aidl::toString(effect).c_str()); + return HalResult<milliseconds>::unsupported(); +} + +HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() { + HalResult<Capabilities> parentResult = HidlHalWrapperV1_2::getCapabilitiesInternal(); + if (!parentResult.isOk()) { + // Loading for previous HAL versions already failed, so propagate failure. + return parentResult; + } + + Capabilities capabilities = parentResult.value(); + auto result = mHandleV1_3->supportsExternalControl(); + capabilities |= result.withDefault(false) ? Capabilities::EXTERNAL_CONTROL : Capabilities::NONE; + return HalResult<Capabilities>::fromReturn(result, capabilities); +} + +HalResult<milliseconds> HidlHalWrapperV1_3::performInternalV1_3( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + V1_3::Effect e = static_cast<V1_3::Effect>(effect); + return performInternal(&V1_3::IVibrator::perform_1_3, mHandleV1_3, e, strength, + completionCallback); +} + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h new file mode 100644 index 0000000000..2c194b5526 --- /dev/null +++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H +#define ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H + +#include <android-base/thread_annotations.h> +#include <chrono> +#include <condition_variable> +#include <queue> +#include <thread> + +namespace android { + +namespace vibrator { + +// Wrapper for a callback to be executed after a delay. +class DelayedCallback { +public: + using Timestamp = std::chrono::time_point<std::chrono::steady_clock>; + + DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay) + : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {} + ~DelayedCallback() = default; + + void run() const; + bool isExpired() const; + Timestamp getExpiration() const; + + // Compare by expiration time, where A < B when A expires first. + bool operator<(const DelayedCallback& other) const; + bool operator>(const DelayedCallback& other) const; + +private: + std::function<void()> mCallback; + Timestamp mExpiration; +}; + +// Schedules callbacks to be executed after a delay. +class CallbackScheduler { +public: + CallbackScheduler() : mCallbackThread(nullptr), mFinished(false) {} + virtual ~CallbackScheduler(); + + virtual void schedule(std::function<void()> callback, std::chrono::milliseconds delay); + +private: + std::condition_variable_any mCondition; + std::mutex mMutex; + + // Lazily instantiated only at the first time this scheduler is used. + std::unique_ptr<std::thread> mCallbackThread; + + // Used to quit the callback thread when this instance is being destroyed. + bool mFinished GUARDED_BY(mMutex); + + // Priority queue with reverse comparator, so tasks that expire first will be on top. + std::priority_queue<DelayedCallback, std::vector<DelayedCallback>, + std::greater<DelayedCallback>> + mQueue GUARDED_BY(mMutex); + + void loop(); +}; + +}; // namespace vibrator + +}; // namespace android + +#endif // ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h new file mode 100644 index 0000000000..0f9aacbbea --- /dev/null +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_VIBRATORHALWRAPPER_H +#define ANDROID_OS_VIBRATORHALWRAPPER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/vibrator/1.3/IVibrator.h> +#include <android/hardware/vibrator/IVibrator.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +// Result of a call to the Vibrator HAL wrapper, holding data if successful. +template <typename T> +class HalResult { +public: + static HalResult<T> ok(T value); + static HalResult<T> failed(); + static HalResult<T> unsupported(); + + static HalResult<T> fromStatus(binder::Status status, T data); + static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data); + + template <typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, T data); + + template <typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, + hardware::vibrator::V1_0::Status status, T data); + + // This will throw std::bad_optional_access if this result is not ok. + const T& value() const { return mValue.value(); } + bool isOk() const { return !mUnsupported && mValue.has_value(); } + bool isFailed() const { return !mUnsupported && !mValue.has_value(); } + bool isUnsupported() const { return mUnsupported; } + +private: + std::optional<T> mValue; + bool mUnsupported; + + explicit HalResult(T value) : mValue(std::make_optional(value)), mUnsupported(false) {} + explicit HalResult(bool unsupported) : mValue(), mUnsupported(unsupported) {} +}; + +// Empty result of a call to the Vibrator HAL wrapper. +template <> +class HalResult<void> { +public: + static HalResult<void> ok(); + static HalResult<void> failed(); + static HalResult<void> unsupported(); + + static HalResult<void> fromStatus(binder::Status status); + static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status); + + template <typename R> + static HalResult<void> fromReturn(hardware::Return<R>& ret); + + bool isOk() const { return !mUnsupported && !mFailed; } + bool isFailed() const { return !mUnsupported && mFailed; } + bool isUnsupported() const { return mUnsupported; } + +private: + bool mFailed; + bool mUnsupported; + + explicit HalResult(bool failed = false, bool unsupported = false) + : mFailed(failed), mUnsupported(unsupported) {} +}; + +// ------------------------------------------------------------------------------------------------- + +// Vibrator HAL capabilities. +enum class Capabilities : int32_t { + NONE = 0, + ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK, + PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK, + AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL, + EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL, + EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, + COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS, + ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL +}; + +inline Capabilities operator|(Capabilities lhs, Capabilities rhs) { + using underlying = typename std::underlying_type<Capabilities>::type; + return static_cast<Capabilities>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs)); +} + +inline Capabilities& operator|=(Capabilities& lhs, Capabilities rhs) { + return lhs = lhs | rhs; +} + +inline Capabilities operator&(Capabilities lhs, Capabilities rhs) { + using underlying = typename std::underlying_type<Capabilities>::type; + return static_cast<Capabilities>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs)); +} + +inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) { + return lhs = lhs & rhs; +} + +// ------------------------------------------------------------------------------------------------- + +// Wrapper for Vibrator HAL handlers. +class HalWrapper { +public: + explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler) + : mCallbackScheduler(std::move(scheduler)) {} + virtual ~HalWrapper() = default; + + virtual HalResult<void> ping() = 0; + + virtual HalResult<void> on(std::chrono::milliseconds timeout, + const std::function<void()>& completionCallback) = 0; + virtual HalResult<void> off() = 0; + + virtual HalResult<void> setAmplitude(int32_t amplitude) = 0; + virtual HalResult<void> setExternalControl(bool enabled) = 0; + + virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, + hardware::vibrator::EffectStrength strength) = 0; + virtual HalResult<void> alwaysOnDisable(int32_t id) = 0; + + virtual HalResult<Capabilities> getCapabilities() = 0; + virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0; + + virtual HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) = 0; + + virtual HalResult<void> performComposedEffect( + const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) = 0; + +protected: + // Shared pointer to allow CallbackScheduler to outlive this wrapper. + const std::shared_ptr<CallbackScheduler> mCallbackScheduler; +}; + +// Wrapper for the AIDL Vibrator HAL. +class AidlHalWrapper : public HalWrapper { +public: + AidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::IVibrator> handle) + : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {} + virtual ~AidlHalWrapper() = default; + + virtual HalResult<void> ping() override; + + virtual HalResult<void> on(std::chrono::milliseconds timeout, + const std::function<void()>& completionCallback) override; + virtual HalResult<void> off() override; + + virtual HalResult<void> setAmplitude(int32_t amplitude) override; + virtual HalResult<void> setExternalControl(bool enabled) override; + + virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, + hardware::vibrator::EffectStrength strength) override; + virtual HalResult<void> alwaysOnDisable(int32_t id) override; + + virtual HalResult<Capabilities> getCapabilities() override; + virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override; + + virtual HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override; + + virtual HalResult<void> performComposedEffect( + const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) override; + +private: + const sp<hardware::vibrator::IVibrator> mHandle; + std::mutex mCapabilitiesMutex; + std::mutex mSupportedEffectsMutex; + std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex); + std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects + GUARDED_BY(mSupportedEffectsMutex); + + // Loads directly from IVibrator handle, skipping caches. + HalResult<Capabilities> getCapabilitiesInternal(); + HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal(); +}; + +// Wrapper for the HDIL Vibrator HAL v1.0. +class HidlHalWrapperV1_0 : public HalWrapper { +public: + HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::V1_0::IVibrator> handle) + : HalWrapper(std::move(scheduler)), mHandleV1_0(std::move(handle)) {} + virtual ~HidlHalWrapperV1_0() = default; + + virtual HalResult<void> ping() override; + + virtual HalResult<void> on(std::chrono::milliseconds timeout, + const std::function<void()>& completionCallback) override; + virtual HalResult<void> off() override; + + virtual HalResult<void> setAmplitude(int32_t amplitude) override; + virtual HalResult<void> setExternalControl(bool enabled) override; + + virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, + hardware::vibrator::EffectStrength strength) override; + virtual HalResult<void> alwaysOnDisable(int32_t id) override; + + virtual HalResult<Capabilities> getCapabilities() override; + virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override; + + virtual HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override; + + virtual HalResult<void> performComposedEffect( + const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) override; + +protected: + const sp<hardware::vibrator::V1_0::IVibrator> mHandleV1_0; + std::mutex mCapabilitiesMutex; + std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex); + + // Loads directly from IVibrator handle, skipping the mCapabilities cache. + virtual HalResult<Capabilities> getCapabilitiesInternal(); + + template <class I, class T> + using perform_fn = + hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength, + hardware::vibrator::V1_0::IVibrator::perform_cb); + + template <class I, class T> + HalResult<std::chrono::milliseconds> performInternal( + perform_fn<I, T> performFn, sp<I> handle, T effect, + hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback); + + HalResult<std::chrono::milliseconds> performInternalV1_0( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback); +}; + +// Wrapper for the HDIL Vibrator HAL v1.1. +class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 { +public: + HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::V1_0::IVibrator> handleV1_0) + : HidlHalWrapperV1_0(std::move(scheduler), handleV1_0), + mHandleV1_1(hardware::vibrator::V1_1::IVibrator::castFrom(handleV1_0)) {} + virtual ~HidlHalWrapperV1_1() = default; + + virtual HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override; + +protected: + const sp<hardware::vibrator::V1_1::IVibrator> mHandleV1_1; + + HalResult<std::chrono::milliseconds> performInternalV1_1( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback); +}; + +// Wrapper for the HDIL Vibrator HAL v1.2. +class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 { +public: + HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::V1_0::IVibrator> handleV1_0) + : HidlHalWrapperV1_1(std::move(scheduler), handleV1_0), + mHandleV1_2(hardware::vibrator::V1_2::IVibrator::castFrom(handleV1_0)) {} + virtual ~HidlHalWrapperV1_2() = default; + + virtual HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override; + +protected: + const sp<hardware::vibrator::V1_2::IVibrator> mHandleV1_2; + + HalResult<std::chrono::milliseconds> performInternalV1_2( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback); +}; + +// Wrapper for the HDIL Vibrator HAL v1.3. +class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 { +public: + HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::V1_0::IVibrator> handleV1_0) + : HidlHalWrapperV1_2(std::move(scheduler), handleV1_0), + mHandleV1_3(hardware::vibrator::V1_3::IVibrator::castFrom(handleV1_0)) {} + virtual ~HidlHalWrapperV1_3() = default; + + virtual HalResult<void> setExternalControl(bool enabled) override; + + virtual HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override; + +protected: + const sp<hardware::vibrator::V1_3::IVibrator> mHandleV1_3; + + virtual HalResult<Capabilities> getCapabilitiesInternal() override; + HalResult<std::chrono::milliseconds> performInternalV1_3( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback); +}; + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android + +#endif // ANDROID_OS_VIBRATORHALWRAPPER_H diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp new file mode 100644 index 0000000000..fa399ad286 --- /dev/null +++ b/services/vibratorservice/test/Android.bp @@ -0,0 +1,47 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libvibratorservice_test", + test_suites: ["device-tests"], + srcs: [ + "VibratorCallbackSchedulerTest.cpp", + "VibratorHalWrapperAidlTest.cpp", + "VibratorHalWrapperHidlV1_0Test.cpp", + "VibratorHalWrapperHidlV1_1Test.cpp", + "VibratorHalWrapperHidlV1_2Test.cpp", + "VibratorHalWrapperHidlV1_3Test.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + shared_libs: [ + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libvibratorservice", + "libutils", + "android.hardware.vibrator-cpp", + "android.hardware.vibrator@1.0", + "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", + "android.hardware.vibrator@1.3", + ], + static_libs: [ + "libgmock", + ], +} diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp new file mode 100644 index 0000000000..aaeb8f990a --- /dev/null +++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperAidlTest" + +#include <android-base/thread_annotations.h> +#include <android/hardware/vibrator/IVibrator.h> +#include <condition_variable> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> + +using std::chrono::milliseconds; +using std::chrono::steady_clock; +using std::chrono::time_point; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class VibratorCallbackSchedulerTest : public Test { +public: + void SetUp() override { + mScheduler = std::make_unique<vibrator::CallbackScheduler>(); + std::lock_guard<std::mutex> lock(mMutex); + mExpiredCallbacks.clear(); + } + +protected: + std::mutex mMutex; + std::condition_variable_any mCondition; + std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr; + std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex); + + std::function<void()> createCallback(int32_t id) { + return [=]() { + { + std::lock_guard<std::mutex> lock(mMutex); + mExpiredCallbacks.push_back(id); + } + mCondition.notify_all(); + }; + } + + std::vector<int32_t> getExpiredCallbacks() { + std::lock_guard<std::mutex> lock(mMutex); + return std::vector<int32_t>(mExpiredCallbacks); + } + + bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) { + time_point<steady_clock> expiration = steady_clock::now() + timeout; + while (steady_clock::now() < expiration) { + std::lock_guard<std::mutex> lock(mMutex); + if (callbackCount <= mExpiredCallbacks.size()) { + return true; + } + mCondition.wait_until(mMutex, expiration); + } + return false; + } +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) { + mScheduler->schedule(createCallback(1), 15ms); + + // Not triggered before delay. + ASSERT_FALSE(waitForCallbacks(1, 10ms)); + ASSERT_TRUE(getExpiredCallbacks().empty()); + + ASSERT_TRUE(waitForCallbacks(1, 10ms)); + ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1)); +} + +TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) { + mScheduler->schedule(createCallback(1), 10ms); + mScheduler->schedule(createCallback(2), 5ms); + mScheduler->schedule(createCallback(3), 1ms); + + ASSERT_TRUE(waitForCallbacks(3, 15ms)); + ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1)); +} + +TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) { + std::vector<std::thread> threads; + for (int i = 0; i < 5; i++) { + threads.push_back(std::thread( + [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + ASSERT_TRUE(waitForCallbacks(5, 25ms)); + ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4)); +} + +TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) { + mScheduler->schedule(createCallback(1), 5ms); + mScheduler.reset(nullptr); + + // Should time out waiting for callback to run. + ASSERT_FALSE(waitForCallbacks(1, 10ms)); + ASSERT_TRUE(getExpiredCallbacks().empty()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp new file mode 100644 index 0000000000..0f2d7bc9ab --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperAidlTest" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +using android::binder::Status; + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; +using android::hardware::vibrator::IVibrator; +using android::hardware::vibrator::IVibratorCallback; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockBinder : public BBinder { +public: + MOCK_METHOD(status_t, linkToDeath, + (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override)); + MOCK_METHOD(status_t, unlinkToDeath, + (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, + wp<DeathRecipient>* outRecipient), + (override)); + MOCK_METHOD(status_t, pingBinder, (), (override)); +}; + +class MockIVibrator : public IVibrator { +public: + MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override)); + MOCK_METHOD(Status, off, (), (override)); + MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override)); + MOCK_METHOD(Status, perform, + (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret), + (override)); + MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override)); + MOCK_METHOD(Status, setAmplitude, (float amplitude), (override)); + MOCK_METHOD(Status, setExternalControl, (bool enabled), (override)); + MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override)); + MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override)); + MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret), + (override)); + MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override)); + MOCK_METHOD(Status, compose, + (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb), + (override)); + MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override)); + MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override)); + MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override)); + MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); + MOCK_METHOD(std::string, getInterfaceHash, (), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperAidlTest : public Test { +public: + void SetUp() override { + mMockBinder = new StrictMock<MockBinder>(); + mMockHal = new StrictMock<MockIVibrator>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibrator>> mMockHal = nullptr; + sp<StrictMock<MockBinder>> mMockBinder = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +ACTION(TriggerCallbackInArg1) { + if (arg1 != nullptr) { + arg1->onComplete(); + } +} + +ACTION(TriggerCallbackInArg2) { + if (arg2 != nullptr) { + arg2->onComplete(); + } +} + +TEST_F(VibratorHalWrapperAidlTest, TestPing) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), onAsBinder()) + .Times(Exactly(1)) + .WillRepeatedly(Return(mMockBinder.get())); + EXPECT_CALL(*mMockBinder.get(), pingBinder()).Times(Exactly(1)); + } + + ASSERT_TRUE(mWrapper->ping().isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), on(Eq(10), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), on(Eq(100), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(10ms, callback).isOk()); + ASSERT_EQ(1, *callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(100ms, callback).isUnsupported()); + // Callback not triggered for unsupported + ASSERT_EQ(1, *callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed()); + // Callback not triggered on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), on(Eq(10), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status())); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), on(Eq(11), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), on(Eq(12), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(10ms, callback).isOk()); + ASSERT_EQ(1, *callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(11ms, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestOff) { + EXPECT_CALL(*mMockHal.get(), off()) + .Times(Exactly(3)) + .WillOnce(Return(Status())) + .WillOnce( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + + ASSERT_TRUE(mWrapper->off().isOk()); + ASSERT_TRUE(mWrapper->off().isUnsupported()); + ASSERT_TRUE(mWrapper->off().isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.1, 1e-2))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.2, 1e-2))) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.5, 1e-2))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 10).isOk()); + ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 5).isUnsupported()); + ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 2).isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false))) + .Times(Exactly(2)) + .WillOnce(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + ASSERT_TRUE(mWrapper->setExternalControl(true).isOk()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM))) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), + alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT); + ASSERT_TRUE(result.isOk()); + result = mWrapper->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->alwaysOnEnable(3, Effect::POP, EffectStrength::STRONG); + ASSERT_TRUE(result.isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2))) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk()); + ASSERT_TRUE(mWrapper->alwaysOnDisable(2).isUnsupported()); + ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) { + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(3)) + .WillOnce( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + + ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported()); + ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) { + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) { + std::vector<Effect> supportedEffects; + supportedEffects.push_back(Effect::CLICK); + supportedEffects.push_back(Effect::TICK); + + EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_)) + .Times(Exactly(3)) + .WillOnce( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status()))); + + ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported()); + ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed()); + + auto result = mWrapper->getSupportedEffects(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedEffects, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) { + std::vector<Effect> supportedEffects; + supportedEffects.push_back(Effect::CLICK); + supportedEffects.push_back(Effect::TICK); + + EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status()))); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getSupportedEffects(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedEffects, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getSupportedEffects(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedEffects, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(1000ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + // Callback not triggered for unsupported + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + // Callback not triggered on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status()))); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) { + std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects; + singleEffect.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f)); + multipleEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f)); + multipleEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)); + + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performComposedEffect(emptyEffects, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performComposedEffect(singleEffect, callback); + ASSERT_TRUE(result.isUnsupported()); + // Callback not triggered for unsupported + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performComposedEffect(multipleEffects, callback); + ASSERT_TRUE(result.isFailed()); + // Callback not triggered on failure + ASSERT_EQ(1, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp new file mode 100644 index 0000000000..7eb4059279 --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperHidlV1_0Test" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +namespace V1_0 = android::hardware::vibrator::V1_0; + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; +using android::hardware::vibrator::IVibrator; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibratorV1_0 : public V1_0::IVibrator { +public: + MOCK_METHOD(hardware::Return<void>, ping, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); + MOCK_METHOD(hardware::Return<void>, perform, + (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperHidlV1_0Test : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIVibratorV1_0>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) { + EXPECT_CALL(*mMockHal.get(), ping()) + .Times(Exactly(2)) + .WillOnce([]() { return hardware::Return<void>(); }) + .WillRepeatedly([]() { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + ASSERT_TRUE(mWrapper->ping().isOk()); + ASSERT_TRUE(mWrapper->ping().isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1)))) + .Times(Exactly(1)) + .WillRepeatedly( + [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint32_t) { + return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); + }); + EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint32_t) { + return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); + }); + EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint32_t) { + return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(1ms, callback).isOk()); + ASSERT_EQ(1, *callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed()); + ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) { + EXPECT_CALL(*mMockHal.get(), off()) + .Times(Exactly(4)) + .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }) + .WillOnce([]() { + return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); + }) + .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); }) + .WillRepeatedly([]() { + return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); + }); + + ASSERT_TRUE(mWrapper->off().isOk()); + ASSERT_TRUE(mWrapper->off().isUnsupported()); + ASSERT_TRUE(mWrapper->off().isFailed()); + ASSERT_TRUE(mWrapper->off().isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), setAmplitude(static_cast<uint8_t>(1))) + .Times(Exactly(1)) + .WillRepeatedly( + [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }); + EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint8_t) { + return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); + }); + EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint8_t) { + return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); + }); + EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint8_t) { + return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); + }); + } + + ASSERT_TRUE(mWrapper->setAmplitude(1).isOk()); + ASSERT_TRUE(mWrapper->setAmplitude(2).isUnsupported()); + ASSERT_TRUE(mWrapper->setAmplitude(3).isFailed()); + ASSERT_TRUE(mWrapper->setAmplitude(4).isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) { + ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) { + ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) { + ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) { + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(2)) + .WillOnce([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + + ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) { + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() { + return hardware::Return<bool>(false); + }); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::NONE, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) { + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() { + return hardware::Return<bool>(true); + }); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) { + ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { + cb(V1_0::Status::UNSUPPORTED_OPERATION, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _)) + .Times(Exactly(2)) + .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { + cb(V1_0::Status::BAD_VALUE, 10); + return hardware::Return<void>(); + }) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) { + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + // Using TICK that is only available in v1.1 + auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isUnsupported()); + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) { + std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects; + singleEffect.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f)); + multipleEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f)); + multipleEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)); + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported()); + + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp new file mode 100644 index 0000000000..d887efce80 --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperHidlV1_1Test" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; + +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibratorV1_1 : public V1_1::IVibrator { +public: + MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); + MOCK_METHOD(hardware::Return<void>, perform, + (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_1, + (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), + (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperHidlV1_1Test : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIVibratorV1_1>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_1::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _)) + .Times(Exactly(1)) + .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_1::perform_cb cb) { + cb(V1_0::Status::UNSUPPORTED_OPERATION, 0); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _)) + .Times(Exactly(2)) + .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_1::perform_cb cb) { + cb(V1_0::Status::BAD_VALUE, 0); + return hardware::Return<void>(); + }) + .WillRepeatedly( + [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) { + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + // Using THUD that is only available in v1.2 + auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isUnsupported()); + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp new file mode 100644 index 0000000000..26d93503c6 --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperHidlV1_2Test" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; +namespace V1_2 = android::hardware::vibrator::V1_2; + +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibratorV1_2 : public V1_2::IVibrator { +public: + MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); + MOCK_METHOD(hardware::Return<void>, perform, + (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_1, + (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), + (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_2, + (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperHidlV1_2Test : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIVibratorV1_2>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), + perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::UNSUPPORTED_OPERATION, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockHal.get(), + perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _)) + .Times(Exactly(2)) + .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::BAD_VALUE, 10); + return hardware::Return<void>(); + }) + .WillRepeatedly( + [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) { + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + // Using TEXTURE_TICK that is only available in v1.3 + auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isUnsupported()); + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp new file mode 100644 index 0000000000..5de6257176 --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperHidlV1_3Test" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; +namespace V1_2 = android::hardware::vibrator::V1_2; +namespace V1_3 = android::hardware::vibrator::V1_3; + +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; +using android::hardware::vibrator::IVibrator; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibratorV1_3 : public V1_3::IVibrator { +public: + MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override)); + MOCK_METHOD(hardware::Return<void>, perform, + (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_1, + (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), + (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_2, + (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_3, + (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperHidlV1_3Test : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIVibratorV1_3>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))) + .Times(Exactly(2)) + .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }) + .WillRepeatedly([]() { + return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); + }); + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false))) + .Times(Exactly(2)) + .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); }) + .WillRepeatedly([]() { + return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); + }); + } + + ASSERT_TRUE(mWrapper->setExternalControl(true).isOk()); + ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(true); + }); + } + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL, + result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(true); + }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(false); + }); + } + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(false); + }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(true); + }); + } + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(false); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(false); + }); + } + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::NONE, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }); + } + + ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); + ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(false); + }); + } + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(false); }); + } + + // Call to supportsAmplitudeControl failed. + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isFailed()); + + // Call to supportsExternalControl failed. + result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isFailed()); + + // Returns successful result from third call. + result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + + // Returns cached successful result. + result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), + perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM), + _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::UNSUPPORTED_OPERATION, 0); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockHal.get(), + perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG), + _)) + .Times(Exactly(2)) + .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::BAD_VALUE, 0); + return hardware::Return<void>(); + }) + .WillRepeatedly( + [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h new file mode 100644 index 0000000000..8d0b22e14c --- /dev/null +++ b/services/vibratorservice/test/test_utils.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIBRATORSERVICE_UNITTEST_UTIL_H_ +#define VIBRATORSERVICE_UNITTEST_UTIL_H_ + +#include <android/hardware/vibrator/IVibrator.h> + +#include <vibratorservice/VibratorHalWrapper.h> + +namespace android { + +namespace vibrator { + +using ::android::hardware::vibrator::CompositeEffect; +using ::android::hardware::vibrator::CompositePrimitive; + +// ------------------------------------------------------------------------------------------------- + +class MockCallbackScheduler : public vibrator::CallbackScheduler { +public: + MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay), + (override)); +}; + +ACTION(TriggerSchedulerCallback) { + arg0(); +} + +// ------------------------------------------------------------------------------------------------- + +class TestFactory { +public: + static CompositeEffect createCompositeEffect(CompositePrimitive primitive, + std::chrono::milliseconds delay, float scale) { + CompositeEffect effect; + effect.primitive = primitive; + effect.delayMs = delay.count(); + effect.scale = scale; + return effect; + } + + static std::function<void()> createCountingCallback(int32_t* counter) { + return [counter]() { *counter += 1; }; + } + +private: + TestFactory() = delete; + ~TestFactory() = delete; +}; + +// ------------------------------------------------------------------------------------------------- + +} // namespace vibrator + +} // namespace android + +#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index d3ed88d2eb..c7ff6405f1 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -970,7 +970,6 @@ VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice, strerror(-err), err); } - // TODO(b/143294545): Return something better than "whole window" pRects[0].offset.x = 0; pRects[0].offset.y = 0; pRects[0].extent = VkExtent2D{static_cast<uint32_t>(width), |