1/*
2 * Copyright (c) 2021-2023 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 "charger_thread.h"
17#include "battery_config.h"
18#include "charger_log.h"
19#include "charger_animation.h"
20#include "init_reboot.h"
21#include <cstdint>
22#include <input_manager.h>
23#include <cinttypes>
24#include <linux/netlink.h>
25#include <parameters.h>
26#include <securec.h>
27
28using namespace OHOS::MMI;
29
30namespace OHOS {
31namespace PowerMgr {
32namespace {
33constexpr int32_t SEC_TO_MSEC = 1000;
34constexpr int32_t NSEC_TO_MSEC = 1000000;
35constexpr int32_t REBOOT_TIME = 2000;
36constexpr int32_t BACKLIGHT_OFF_TIME_MS = 10000;
37constexpr int32_t VIBRATE_TIME_MS = 75;
38const std::string REBOOT_CMD = "";
39const std::string SHUTDOWN_CMD = "shutdown";
40constexpr int32_t KEY_ACTION_DOWN_VAL = 1;
41constexpr int32_t KEY_ACTION_UP_VAL = 0;
42} // namespace
43
44std::unique_ptr<ChargerAnimation> ChargerThread::animation_ = nullptr;
45bool ChargerThread::isChargeStateChanged_ = false;
46bool ChargerThread::isConfigParse_ = false;
47int32_t ChargerThread::lackPowerCapacity_ = -1;
48
49struct KeyState {
50    bool isUp;
51    bool isDown;
52    int64_t timestamp;
53};
54struct KeyState g_keys[KEY_MAX + 1] = {};
55
56static int64_t GetCurrentTime()
57{
58    timespec tm {};
59    clock_gettime(CLOCK_MONOTONIC, &tm);
60    return tm.tv_sec * SEC_TO_MSEC + (tm.tv_nsec / NSEC_TO_MSEC);
61}
62
63void ChargerThreadInputMonitor::SetKeyState(int32_t code, int32_t value, int64_t now) const
64{
65    bool isDown = !!value;
66
67    if (code > KEY_MAX) {
68        BATTERY_HILOGW(FEATURE_CHARGING, "code lager than KEY_MAX: %{public}d", code);
69        return;
70    }
71
72    if (g_keys[code].isDown == isDown) {
73        BATTERY_HILOGW(FEATURE_CHARGING, "PowerKey is already down");
74        return;
75    }
76
77    if (isDown) {
78        g_keys[code].timestamp = now;
79    }
80
81    g_keys[code].isDown = isDown;
82    g_keys[code].isUp = true;
83}
84
85void ChargerThread::HandleStates()
86{
87    HandleChargingState();
88    HandlePowerKeyState();
89    HandleScreenState();
90}
91
92int32_t ChargerThread::UpdateWaitInterval()
93{
94    int64_t currentTime = GetCurrentTime();
95    int64_t nextWait = INT64_MAX;
96    int64_t timeout = INVALID;
97
98    if (keyWait_ != INVALID && keyWait_ < nextWait) {
99        nextWait = keyWait_;
100    }
101
102    if (backlightWait_ != INVALID && backlightWait_ < nextWait) {
103        nextWait = backlightWait_;
104    }
105
106    if (nextWait != INVALID && nextWait != INT64_MAX) {
107        if (nextWait - currentTime > 0) {
108            timeout = nextWait - currentTime;
109        } else {
110            timeout = 0;
111        }
112    }
113
114    return static_cast<int32_t>(timeout);
115}
116
117void ChargerThread::CycleMatters()
118{
119    if (!started_) {
120        started_ = true;
121        backlightWait_ = GetCurrentTime() - 1;
122    }
123
124    UpdateBatteryInfo(nullptr);
125    BATTERY_HILOGD(FEATURE_CHARGING, "chargeState_=%{public}d, capacity_=%{public}d", chargeState_, capacity_);
126    UpdateEpollInterval(chargeState_);
127}
128
129void ChargerThread::UpdateBatteryInfo(void* arg)
130{
131    BATTERY_HILOGD(FEATURE_CHARGING, "start update battery info by provider");
132    int32_t temperature = 0;
133    provider_->ParseTemperature(&temperature);
134    provider_->ParseCapacity(&capacity_);
135    int32_t oldChargeState = chargeState_;
136    provider_->ParseChargeState(&chargeState_);
137    BATTERY_HILOGD(FEATURE_CHARGING, "temperature=%{public}d, capacity_=%{public}d, chargeState_=%{public}d",
138        temperature, capacity_, chargeState_);
139    if (chargeState_ != oldChargeState) {
140        isChargeStateChanged_ = true;
141    } else {
142        isChargeStateChanged_ = false;
143    }
144
145    HandleTemperature(temperature);
146    HandleCapacity(capacity_);
147
148    led_->UpdateColor(chargeState_, capacity_);
149
150    if (backlight_->GetScreenState() == BatteryBacklight::SCREEN_ON) {
151        UpdateAnimation(chargeState_, capacity_);
152    }
153}
154
155void ChargerThread::HandleTemperature(const int32_t& temperature)
156{
157    const int32_t DEFAULT_UPPER_TEMP_CONF = INT32_MAX;
158    const int32_t DEFAULT_LOWER_TEMP_CONF = INT32_MIN;
159    auto& batteryConfig = BatteryConfig::GetInstance();
160    auto highTemp = batteryConfig.GetInt("temperature.high", DEFAULT_UPPER_TEMP_CONF);
161    auto lowTemp = batteryConfig.GetInt("temperature.low", DEFAULT_LOWER_TEMP_CONF);
162    BATTERY_HILOGD(FEATURE_CHARGING, "temperature=%{public}d, lowTemp=%{public}d, highTemp=%{public}d", temperature,
163        lowTemp, highTemp);
164
165    if (((temperature <= lowTemp) || (temperature >= highTemp)) && (lowTemp != highTemp)) {
166        BATTERY_HILOGW(FEATURE_CHARGING, "temperature out of range, shutdown device");
167        DoReboot(SHUTDOWN_CMD.c_str());
168    }
169}
170
171void ChargerThread::HandleCapacity(const int32_t& capacity)
172{
173    if (capacity > lackPowerCapacity_ &&
174        (chargeState_ == PowerSupplyProvider::CHARGE_STATE_DISABLE ||
175            chargeState_ == PowerSupplyProvider::CHARGE_STATE_NONE)) {
176        BATTERY_HILOGW(FEATURE_CHARGING, "Not Charging, Shutdown system");
177        DoReboot(SHUTDOWN_CMD.c_str());
178    }
179}
180
181void ChargerThread::UpdateAnimation(const int32_t& chargeState, const int32_t& capacity)
182{
183    BATTERY_HILOGD(FEATURE_CHARGING, "start update animation, capacity=%{public}d", capacity);
184    if ((chargeState == PowerSupplyProvider::CHARGE_STATE_NONE) ||
185        (chargeState == PowerSupplyProvider::CHARGE_STATE_RESERVED)) {
186        BATTERY_HILOGD(FEATURE_CHARGING, "Unknown charge state");
187        return;
188    }
189
190    if (capacity <= lackPowerCapacity_) {
191        if (chargeState == PowerSupplyProvider::CHARGE_STATE_ENABLE) { // Charging state
192            BATTERY_HILOGD(FEATURE_CHARGING, "Lack power");
193            animation_->AnimationStop();
194            animation_->LackPowerNotChargingPromptStop();
195            animation_->LackPowerChargingPromptStart();
196        } else if (chargeState == PowerSupplyProvider::CHARGE_STATE_DISABLE) { // Not charging state
197            BATTERY_HILOGD(FEATURE_CHARGING, "Lack power, please connect charger");
198            animation_->AnimationStop();
199            animation_->LackPowerChargingPromptStop();
200            animation_->LackPowerNotChargingPromptStart();
201        } else {
202            BATTERY_HILOGD(FEATURE_CHARGING, "capacity=%{public}d, chargeState=%{public}d", capacity, chargeState);
203        }
204    } else if (chargeState == PowerSupplyProvider::CHARGE_STATE_ENABLE ||
205        chargeState == PowerSupplyProvider::CHARGE_STATE_FULL) { // Charging state
206        BATTERY_HILOGD(FEATURE_CHARGING, "Display animation according capacity");
207        animation_->LackPowerChargingPromptStop();
208        animation_->LackPowerNotChargingPromptStop();
209        animation_->AnimationStart(capacity);
210    }
211}
212
213void ChargerThread::InitAnimation()
214{
215    animation_ = std::make_unique<ChargerAnimation>();
216    animation_->InitConfig();
217}
218
219void ChargerThread::SetKeyWait(struct KeyState& key, int64_t timeout)
220{
221    int64_t nextMoment = key.timestamp + timeout;
222    if (keyWait_ == INVALID || nextMoment < keyWait_) {
223        keyWait_ = nextMoment;
224    }
225}
226
227void ChargerThread::HandleChargingState()
228{
229    if ((chargeState_ == PowerSupplyProvider::CHARGE_STATE_NONE) ||
230        (chargeState_ == PowerSupplyProvider::CHARGE_STATE_RESERVED)) {
231        return;
232    }
233
234    if (isChargeStateChanged_) {
235        BATTERY_HILOGD(FEATURE_CHARGING, "Charging State has changed");
236        backlight_->TurnOnScreen();
237        backlightWait_ = GetCurrentTime() - 1;
238        UpdateAnimation(chargeState_, capacity_);
239        isChargeStateChanged_ = false;
240    }
241}
242
243void ChargerThread::HandleScreenState()
244{
245    if (backlightWait_ != INVALID && GetCurrentTime() > backlightWait_ + BACKLIGHT_OFF_TIME_MS) {
246        backlight_->TurnOffScreen();
247        animation_->AnimationStop();
248        animation_->LackPowerChargingPromptStop();
249        animation_->LackPowerNotChargingPromptStop();
250        backlightWait_ = INVALID;
251    }
252}
253
254void ChargerThread::HandlePowerKeyState()
255{
256    auto now = GetCurrentTime();
257    HandlePowerKey(KEY_POWER, now);
258
259    BATTERY_HILOGD(FEATURE_CHARGING, "keyWait_=%{public}" PRId64 "", keyWait_);
260    if (keyWait_ != INVALID && now > keyWait_) {
261        keyWait_ = INVALID;
262    }
263}
264
265void ChargerThread::HandlePowerKey(int32_t keycode, int64_t now)
266{
267    if (keycode == KEY_POWER) {
268        static bool turnOnByKeydown = false;
269        if (g_keys[keycode].isDown) {
270            int64_t rebootTime = g_keys[keycode].timestamp + REBOOT_TIME;
271            if (now >= rebootTime) {
272                BATTERY_HILOGW(FEATURE_CHARGING, "reboot machine");
273                backlight_->TurnOffScreen();
274                vibrate_->HandleVibration(VIBRATE_TIME_MS);
275                DoReboot(REBOOT_CMD.c_str());
276            } else if (backlight_->GetScreenState() == BatteryBacklight::SCREEN_OFF) {
277                SetKeyWait(g_keys[keycode], REBOOT_TIME);
278                backlight_->TurnOnScreen();
279                UpdateAnimation(chargeState_, capacity_);
280                backlightWait_ = now - 1;
281                turnOnByKeydown = true;
282            }
283        } else if (g_keys[keycode].isUp) {
284            if (backlight_->GetScreenState() == BatteryBacklight::SCREEN_ON && !turnOnByKeydown) {
285                backlight_->TurnOffScreen();
286                animation_->AnimationStop();
287                animation_->LackPowerChargingPromptStop();
288                animation_->LackPowerNotChargingPromptStop();
289                backlightWait_ = INVALID;
290            } else {
291                backlight_->TurnOnScreen();
292                backlightWait_ = now - 1;
293                UpdateAnimation(chargeState_, capacity_);
294            }
295            g_keys[keycode].isUp = false;
296            turnOnByKeydown = false;
297        }
298    }
299}
300
301void ChargerThreadInputMonitor::OnInputEvent(std::shared_ptr<OHOS::MMI::PointerEvent> pointerEvent) const {};
302void ChargerThreadInputMonitor::OnInputEvent(std::shared_ptr<OHOS::MMI::AxisEvent> axisEvent) const {};
303
304void ChargerThreadInputMonitor::OnInputEvent(std::shared_ptr<OHOS::MMI::KeyEvent> keyEvent) const
305{
306    if (keyEvent->GetKeyCode() == OHOS::MMI::KeyEvent::KEYCODE_POWER) {
307        if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_DOWN) {
308            BATTERY_HILOGI(FEATURE_CHARGING, "PowerKey Action Down");
309            SetKeyState(KEY_POWER, KEY_ACTION_DOWN_VAL, GetCurrentTime());
310        } else if (keyEvent->GetKeyAction() == MMI::KeyEvent::KEY_ACTION_UP) {
311            BATTERY_HILOGI(FEATURE_CHARGING, "PowerKey Action Up");
312            SetKeyState(KEY_POWER, KEY_ACTION_UP_VAL, GetCurrentTime());
313        }
314    }
315}
316
317void ChargerThread::InputMonitorInit()
318{
319    BATTERY_HILOGI(FEATURE_CHARGING, "Charger input monitor init");
320    std::shared_ptr<ChargerThreadInputMonitor> inputMonitor = std::make_shared<ChargerThreadInputMonitor>();
321    if (inputMonitorId_ < 0) {
322        inputMonitorId_ =
323            InputManager::GetInstance()->AddMonitor(std::static_pointer_cast<IInputEventConsumer>(inputMonitor));
324    }
325}
326
327void ChargerThread::InputMonitorCancel()
328{
329    BATTERY_HILOGI(FEATURE_CHARGING, "Charger input monitor cancel");
330    InputManager *inputManager = InputManager::GetInstance();
331    if (inputMonitorId_ >= 0) {
332        inputManager->RemoveMonitor(inputMonitorId_);
333        inputMonitorId_ = -1;
334    }
335}
336
337void ChargerThread::InitLackPowerCapacity()
338{
339#ifdef HAS_BATTERY_CONFIG_POLICY_PART
340    if (!isConfigParse_) {
341        isConfigParse_ = BatteryConfig::GetInstance().ParseConfig();
342    }
343#endif
344    auto& batteryConfig = BatteryConfig::GetInstance();
345    lackPowerCapacity_ = batteryConfig.GetInt("soc.shutdown");
346    BATTERY_HILOGD(FEATURE_CHARGING, "lackPowerCapacity_ = %{public}d", lackPowerCapacity_);
347}
348
349void ChargerThread::InitBatteryFileSystem()
350{
351    provider_ = std::make_unique<PowerSupplyProvider>();
352    if (provider_ == nullptr) {
353        BATTERY_HILOGE(FEATURE_CHARGING, "make_unique PowerSupplyProvider return nullptr");
354        return;
355    }
356    provider_->InitBatteryPath();
357    provider_->InitPowerSupplySysfs();
358}
359
360void ChargerThread::InitVibration()
361{
362    vibrate_ = std::make_unique<BatteryVibrate>();
363    if (vibrate_ == nullptr) {
364        BATTERY_HILOGE(FEATURE_CHARGING, "make_unique BatteryVibrate return nullptr");
365        return;
366    }
367
368    if (!vibrate_->InitVibration()) {
369        BATTERY_HILOGW(FEATURE_CHARGING, "InitVibration failed, vibration does not work");
370    }
371}
372
373void ChargerThread::InitBacklight()
374{
375    backlight_ = std::make_unique<BatteryBacklight>();
376    if (backlight_ == nullptr) {
377        BATTERY_HILOGE(FEATURE_CHARGING, "make_unique BatteryBacklight return nullptr");
378        return;
379    }
380    backlight_->TurnOnScreen();
381}
382
383void ChargerThread::InitLed()
384{
385    led_ = std::make_unique<BatteryLed>();
386    if (led_ == nullptr) {
387        BATTERY_HILOGE(FEATURE_CHARGING, "make_unique BatteryLed return nullptr");
388        return;
389    }
390#ifdef HAS_BATTERY_CONFIG_POLICY_PART
391    if (!isConfigParse_) {
392        isConfigParse_ = BatteryConfig::GetInstance().ParseConfig();
393    }
394#endif
395    led_->InitLight();
396    led_->TurnOff();
397}
398
399void ChargerThread::Init()
400{
401    BATTERY_HILOGD(FEATURE_CHARGING, "start init charger thread");
402    InitLackPowerCapacity();
403    InitBatteryFileSystem();
404    InitVibration();
405    InitBacklight();
406    InitLed();
407    InitAnimation();
408    InputMonitorInit();
409}
410
411void ChargerThread::Run(void* service)
412{
413    BATTERY_HILOGI(FEATURE_CHARGING, "start run charger thread");
414    Init();
415    std::make_unique<std::thread>([this, service] { this->LoopingThreadEntry(service); })->join();
416}
417} // namespace PowerMgr
418} // namespace OHOS
419