1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <signal.h>
18 #include <sys/time.h>
19 #include <unistd.h>
20 
21 #include <algorithm>
22 #include <mutex>
23 #include <thread>
24 
25 #include <android-base/logging.h>
26 #include <android-base/parsebool.h>
27 #include <android-base/parseint.h>
28 #include <android-base/properties.h>
29 #include <android-base/thread_annotations.h>
30 
31 #include "property_monitor.h"
32 
33 static constexpr char kAdbWatchdogProperty[] = "persist.adb.watchdog";
34 static constexpr char kTestHarnessProperty[] = "persist.sys.test_harness";
35 
36 static constexpr unsigned int kDefaultAdbWatchdogTimeoutSeconds = 600;
37 static unsigned int g_watchdog_timeout_seconds;
38 
39 static std::mutex g_watchdog_mutex [[clang::no_destroy]];
40 
41 // The watchdog is "running" when it's enabled via property and there are no connected clients.
42 static bool g_watchdog_running GUARDED_BY(g_watchdog_mutex) = false;
43 
44 static PropertyMonitor g_property_monitor [[clang::no_destroy]];
45 
46 static void UpdateWatchdog() REQUIRES(g_watchdog_mutex) {
47     static std::once_flag once;
48     std::call_once(once, []() {
49         signal(SIGALRM, [](int) {
50             execl("/system/bin/reboot", "/system/bin/reboot", "bootloader", nullptr);
51         });
52     });
53 
54     static bool alarm_set = false;
55 
56     static auto Arm = []() {
57         LOG(INFO) << "adb watchdog armed, triggering in " << g_watchdog_timeout_seconds
58                   << " seconds";
59         alarm(g_watchdog_timeout_seconds);
60         alarm_set = true;
61     };
62 
63     static auto Disarm = []() {
64         unsigned int previous = alarm(0);
65         if (previous != 0) {
66             LOG(INFO) << "adb watchdog disarmed with " << previous << " seconds left";
67         }
68         alarm_set = false;
69     };
70 
71     bool watchdog_enabled = android::base::GetBoolProperty(
72             kAdbWatchdogProperty, android::base::GetBoolProperty(kTestHarnessProperty, false));
73     if (!watchdog_enabled) {
74         if (alarm_set) {
75             Disarm();
76         }
77         return;
78     }
79 
80     if (g_watchdog_running) {
81         if (!alarm_set) {
82             Arm();
83         }
84     } else {
85         Disarm();
86     }
87 }
88 
89 namespace watchdog {
90 
91 void Start() {
92     std::lock_guard<std::mutex> lock(g_watchdog_mutex);
93     g_watchdog_running = true;
94     UpdateWatchdog();
95 }
96 
97 void Stop() {
98     std::lock_guard<std::mutex> lock(g_watchdog_mutex);
99     g_watchdog_running = false;
100     UpdateWatchdog();
101 }
102 
103 void Initialize() {
104     for (auto& property : {kAdbWatchdogProperty, kTestHarnessProperty}) {
105         g_property_monitor.Add(property, [property](std::string value) {
106             LOG(INFO) << property << " set to '" << value << "'";
107 
108             std::lock_guard<std::mutex> lock(g_watchdog_mutex);
109             UpdateWatchdog();
110             return true;
111         });
112     }
113 
114     g_property_monitor.Add("persist.adb.watchdog.timeout_secs", [](std::string value) {
115         // This presumably isn't going to change while the watchdog is armed,
116         // so we don't need to recalculate a timer.
117         {
118             std::lock_guard<std::mutex> lock(g_watchdog_mutex);
119             if (!android::base::ParseUint(value, &g_watchdog_timeout_seconds)) {
120                 g_watchdog_timeout_seconds = kDefaultAdbWatchdogTimeoutSeconds;
121             }
122         }
123 
124         LOG(INFO) << "adb watchdog timeout set to " << g_watchdog_timeout_seconds << " seconds";
125         return true;
126     });
127 
128     Start();
129     std::thread([]() { g_property_monitor.Run(); }).detach();
130 }
131 
132 }  // namespace watchdog
133