/* * 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 #include #include #include #include #include #include #include #include #include #include #include "property_monitor.h" static constexpr char kAdbWatchdogProperty[] = "persist.adb.watchdog"; static constexpr char kTestHarnessProperty[] = "persist.sys.test_harness"; static constexpr unsigned int kDefaultAdbWatchdogTimeoutSeconds = 600; static unsigned int g_watchdog_timeout_seconds; static std::mutex g_watchdog_mutex [[clang::no_destroy]]; // The watchdog is "running" when it's enabled via property and there are no connected clients. static bool g_watchdog_running GUARDED_BY(g_watchdog_mutex) = false; static PropertyMonitor g_property_monitor [[clang::no_destroy]]; static void UpdateWatchdog() REQUIRES(g_watchdog_mutex) { static std::once_flag once; std::call_once(once, []() { signal(SIGALRM, [](int) { execl("/system/bin/reboot", "/system/bin/reboot", "bootloader", nullptr); }); }); static bool alarm_set = false; static auto Arm = []() { LOG(INFO) << "adb watchdog armed, triggering in " << g_watchdog_timeout_seconds << " seconds"; alarm(g_watchdog_timeout_seconds); alarm_set = true; }; static auto Disarm = []() { unsigned int previous = alarm(0); if (previous != 0) { LOG(INFO) << "adb watchdog disarmed with " << previous << " seconds left"; } alarm_set = false; }; bool watchdog_enabled = android::base::GetBoolProperty( kAdbWatchdogProperty, android::base::GetBoolProperty(kTestHarnessProperty, false)); if (!watchdog_enabled) { if (alarm_set) { Disarm(); } return; } if (g_watchdog_running) { if (!alarm_set) { Arm(); } } else { Disarm(); } } namespace watchdog { void Start() { std::lock_guard lock(g_watchdog_mutex); g_watchdog_running = true; UpdateWatchdog(); } void Stop() { std::lock_guard lock(g_watchdog_mutex); g_watchdog_running = false; UpdateWatchdog(); } void Initialize() { for (auto& property : {kAdbWatchdogProperty, kTestHarnessProperty}) { g_property_monitor.Add(property, [property](std::string value) { LOG(INFO) << property << " set to '" << value << "'"; std::lock_guard lock(g_watchdog_mutex); UpdateWatchdog(); return true; }); } g_property_monitor.Add("persist.adb.watchdog.timeout_secs", [](std::string value) { // This presumably isn't going to change while the watchdog is armed, // so we don't need to recalculate a timer. { std::lock_guard lock(g_watchdog_mutex); if (!android::base::ParseUint(value, &g_watchdog_timeout_seconds)) { g_watchdog_timeout_seconds = kDefaultAdbWatchdogTimeoutSeconds; } } LOG(INFO) << "adb watchdog timeout set to " << g_watchdog_timeout_seconds << " seconds"; return true; }); Start(); std::thread([]() { g_property_monitor.Run(); }).detach(); } } // namespace watchdog