1/*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "watchdog.h"
17
18#include <parameter.h>
19#include <unistd.h>
20
21#include "app_mgr_client.h"
22#include "app_recovery.h"
23#include "appfreeze_inner.h"
24#include "hisysevent.h"
25#include "hilog_tag_wrapper.h"
26#include "xcollie/watchdog.h"
27
28namespace OHOS {
29namespace AppExecFwk {
30namespace {
31constexpr uint32_t CHECK_MAIN_THREAD_IS_ALIVE = 1;
32constexpr int RESET_RATIO = 2;
33
34constexpr int32_t BACKGROUND_REPORT_COUNT_MAX = 5;
35constexpr int32_t WATCHDOG_REPORT_COUNT_MAX = 5;
36#ifdef SUPPORT_ASAN
37constexpr uint32_t CHECK_INTERVAL_TIME = 45000;
38#else
39constexpr uint32_t CHECK_INTERVAL_TIME = 3000;
40#endif
41}
42std::shared_ptr<EventHandler> Watchdog::appMainHandler_ = nullptr;
43
44Watchdog::Watchdog()
45{}
46
47Watchdog::~Watchdog()
48{
49    if (!stopWatchdog_) {
50        TAG_LOGD(AAFwkTag::APPDFR, "Stop watchdog");
51        OHOS::HiviewDFX::Watchdog::GetInstance().StopWatchdog();
52    }
53}
54
55void Watchdog::Init(const std::shared_ptr<EventHandler> mainHandler)
56{
57    std::unique_lock<std::mutex> lock(cvMutex_);
58    Watchdog::appMainHandler_ = mainHandler;
59    if (appMainHandler_ != nullptr) {
60        TAG_LOGD(AAFwkTag::APPDFR, "Watchdog init send event");
61        appMainHandler_->SendEvent(CHECK_MAIN_THREAD_IS_ALIVE, 0, EventQueue::Priority::IMMEDIATE);
62    }
63    lastWatchTime_ = 0;
64    auto watchdogTask = [this] { this->Timer(); };
65    OHOS::HiviewDFX::Watchdog::GetInstance().RunPeriodicalTask("AppkitWatchdog", watchdogTask,
66        CHECK_INTERVAL_TIME, INI_TIMER_FIRST_SECOND);
67}
68
69void Watchdog::Stop()
70{
71    TAG_LOGD(AAFwkTag::APPDFR, "called");
72    std::unique_lock<std::mutex> lock(cvMutex_);
73    if (stopWatchdog_) {
74        TAG_LOGD(AAFwkTag::APPDFR, "stoped");
75        return;
76    }
77    stopWatchdog_.store(true);
78    cvWatchdog_.notify_all();
79    OHOS::HiviewDFX::Watchdog::GetInstance().StopWatchdog();
80
81    if (appMainHandler_) {
82        appMainHandler_.reset();
83        appMainHandler_ = nullptr;
84    }
85}
86
87void Watchdog::SetAppMainThreadState(const bool appMainThreadState)
88{
89    std::unique_lock<std::mutex> lock(cvMutex_);
90    appMainThreadIsAlive_.store(appMainThreadState);
91}
92
93void Watchdog::SetBundleInfo(const std::string& bundleName, const std::string& bundleVersion)
94{
95    OHOS::HiviewDFX::Watchdog::GetInstance().SetBundleInfo(bundleName, bundleVersion);
96}
97
98void Watchdog::SetBackgroundStatus(const bool isInBackground)
99{
100    std::unique_lock<std::mutex> lock(cvMutex_);
101    isInBackground_.store(isInBackground);
102    OHOS::HiviewDFX::Watchdog::GetInstance().SetForeground(!isInBackground);
103}
104
105void Watchdog::AllowReportEvent()
106{
107    std::unique_lock<std::mutex> lock(cvMutex_);
108    needReport_.store(true);
109    isSixSecondEvent_.store(false);
110    backgroundReportCount_.store(0);
111    watchdogReportCount_.store(0);
112}
113
114bool Watchdog::IsReportEvent()
115{
116    if (appMainThreadIsAlive_) {
117        appMainThreadIsAlive_.store(false);
118        return false;
119    }
120    TAG_LOGD(AAFwkTag::APPDFR, "AppMainThread not alive");
121    return true;
122}
123
124bool Watchdog::IsStopWatchdog()
125{
126    std::unique_lock<std::mutex> lock(cvMutex_);
127    return stopWatchdog_;
128}
129
130void Watchdog::SetBgWorkingThreadStatus(const bool isBgWorkingThread)
131{
132    std::unique_lock<std::mutex> lock(cvMutex_);
133    isBgWorkingThread_.store(isBgWorkingThread);
134}
135
136void Watchdog::Timer()
137{
138    std::unique_lock<std::mutex> lock(cvMutex_);
139    if (stopWatchdog_) {
140        TAG_LOGD(AAFwkTag::APPDFR, "stoped");
141        return;
142    }
143    if (!needReport_) {
144        watchdogReportCount_++;
145        TAG_LOGE(AAFwkTag::APPDFR, "timeout, wait to recover, wait count: %{public}d",
146            watchdogReportCount_.load());
147        if (watchdogReportCount_.load() >= WATCHDOG_REPORT_COUNT_MAX) {
148#ifndef APP_NO_RESPONSE_DIALOG
149            AppExecFwk::AppfreezeInner::GetInstance()->AppfreezeHandleOverReportCount(true);
150#endif
151            watchdogReportCount_.store(0);
152        } else if (watchdogReportCount_.load() >= (WATCHDOG_REPORT_COUNT_MAX - 1)) {
153#ifndef APP_NO_RESPONSE_DIALOG
154            AppExecFwk::AppfreezeInner::GetInstance()->AppfreezeHandleOverReportCount(false);
155#endif
156        }
157        return;
158    }
159
160    if (IsReportEvent()) {
161        const int bufferLen = 128;
162        char paramOutBuf[bufferLen] = {0};
163        const char *hook_mode = "startup:";
164        int ret = GetParameter("libc.hook_mode", "", paramOutBuf, bufferLen);
165        if (ret <= 0 || strncmp(paramOutBuf, hook_mode, strlen(hook_mode)) != 0) {
166            ReportEvent();
167        }
168    }
169    if (appMainHandler_ != nullptr) {
170        appMainHandler_->SendEvent(CHECK_MAIN_THREAD_IS_ALIVE, 0, EventQueue::Priority::IMMEDIATE);
171    }
172    int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
173        system_clock::now().time_since_epoch()).count();
174    if ((now - lastWatchTime_) >= (CHECK_INTERVAL_TIME / RESET_RATIO)) {
175        lastWatchTime_ = now;
176    }
177}
178
179void Watchdog::ReportEvent()
180{
181    if (isBgWorkingThread_) {
182        TAG_LOGD(AAFwkTag::APPDFR, "Thread is working in the background, do not report this time");
183        return;
184    }
185    int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
186        system_clock::now().time_since_epoch()).count();
187    if ((now - lastWatchTime_) > (RESET_RATIO * CHECK_INTERVAL_TIME) ||
188        (now - lastWatchTime_) < (CHECK_INTERVAL_TIME / RESET_RATIO)) {
189        TAG_LOGI(AAFwkTag::APPDFR,
190            "Thread may be blocked, not report time. currTime: %{public}llu, lastTime: %{public}llu",
191            static_cast<unsigned long long>(now), static_cast<unsigned long long>(lastWatchTime_));
192        return;
193    }
194
195    if (isInBackground_ && backgroundReportCount_.load() < BACKGROUND_REPORT_COUNT_MAX) {
196        TAG_LOGI(AAFwkTag::APPDFR, "In Background, thread may be blocked in, not report time"
197            "currTime: %{public}" PRIu64 ", lastTime: %{public}" PRIu64 "",
198            static_cast<uint64_t>(now), static_cast<uint64_t>(lastWatchTime_));
199        backgroundReportCount_++;
200        return;
201    }
202    backgroundReportCount_++;
203
204    if (!needReport_) {
205        return;
206    }
207
208#ifndef APP_NO_RESPONSE_DIALOG
209    if (isSixSecondEvent_) {
210        needReport_.store(false);
211    }
212#endif
213    AppExecFwk::AppfreezeInner::GetInstance()->ThreadBlock(isSixSecondEvent_);
214}
215}  // namespace AppExecFwk
216}  // namespace OHOS
217