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 "work_status.h"
17 
18 #include "time_service_client.h"
19 #include "work_datashare_helper.h"
20 #include "work_sched_errors.h"
21 #include "work_sched_utils.h"
22 #include "work_scheduler_service.h"
23 #include "work_sched_hilog.h"
24 #include "work_sched_errors.h"
25 #ifdef DEVICE_USAGE_STATISTICS_ENABLE
26 #include "bundle_active_client.h"
27 #include "bundle_active_group_map.h"
28 #endif
29 #include "parameters.h"
30 
31 using namespace std;
32 
33 namespace OHOS {
34 namespace WorkScheduler {
35 static const double ONE_SECOND = 1000.0;
36 static bool debugMode = false;
37 static const int64_t MIN_INTERVAL_DEFAULT = 2 * 60 * 60 * 1000;
38 std::map<int32_t, time_t> WorkStatus::s_uid_last_time_map;
39 const int32_t DEFAULT_PRIORITY = 10000;
40 const int32_t HIGH_PRIORITY = 0;
41 const int32_t ACTIVE_GROUP = 10;
42 const string SWITCH_ON = "1";
43 ffrt::mutex WorkStatus::s_uid_last_time_mutex;
44 
getCurrentTime()45 time_t getCurrentTime()
46 {
47     time_t result;
48     time(&result);
49     return result;
50 }
51 
getOppositeTime()52 time_t getOppositeTime()
53 {
54     time_t result;
55     sptr<MiscServices::TimeServiceClient> timer = MiscServices::TimeServiceClient::GetInstance();
56     result = static_cast<time_t>(timer->GetBootTimeMs());
57     return result;
58 }
59 
WorkStatus(WorkInfo &workInfo, int32_t uid)60 WorkStatus::WorkStatus(WorkInfo &workInfo, int32_t uid)
61 {
62     this->workInfo_ = make_shared<WorkInfo>(workInfo);
63     this->workId_ = MakeWorkId(workInfo.GetWorkId(), uid);
64     this->bundleName_ = workInfo.GetBundleName();
65     this->abilityName_ = workInfo.GetAbilityName();
66     this->baseTime_ = workInfo.GetBaseTime();
67     this->uid_ = uid;
68     this->userId_ = WorkSchedUtils::GetUserIdByUid(uid);
69     if (workInfo.GetConditionMap()->count(WorkCondition::Type::TIMER) > 0) {
70         auto workTimerCondition = workInfo.GetConditionMap()->at(WorkCondition::Type::TIMER);
71         shared_ptr<Condition> timeCondition = make_shared<Condition>();
72         timeCondition->uintVal = workTimerCondition->uintVal;
73         timeCondition->boolVal = workTimerCondition->boolVal;
74         if (!workTimerCondition->boolVal) {
75             timeCondition->intVal = workTimerCondition->intVal;
76         }
77         std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
78         conditionMap_.emplace(WorkCondition::Type::TIMER, timeCondition);
79     }
80     this->persisted_ = workInfo.IsPersisted();
81     this->priority_ = GetPriority();
82     this->currentStatus_ = WAIT_CONDITION;
83     this->minInterval_ = MIN_INTERVAL_DEFAULT;
84     this->callbackFlag_ = false;
85 }
86 
~WorkStatus()87 WorkStatus::~WorkStatus() {}
88 
OnConditionChanged(WorkCondition::Type &type, shared_ptr<Condition> value)89 int32_t WorkStatus::OnConditionChanged(WorkCondition::Type &type, shared_ptr<Condition> value)
90 {
91     WS_HILOGD("Work status condition changed.");
92     if (workInfo_->GetConditionMap()->count(type) > 0
93         && type != WorkCondition::Type::TIMER
94         && type != WorkCondition::Type::GROUP) {
95         std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
96         if (conditionMap_.count(type) > 0) {
97             conditionMap_.at(type) = value;
98         } else {
99             conditionMap_.emplace(type, value);
100         }
101     }
102     callbackFlag_ = false;
103     if (type == WorkCondition::Type::GROUP && value && value->boolVal) {
104         WS_HILOGD("Group changed, bundleName: %{public}s.", value->strVal.c_str());
105         callbackFlag_ = true;
106         if (value->intVal == userId_ && value->strVal == bundleName_) {
107             SetMinIntervalByGroup(value->enumVal);
108         } else {
109             return E_GROUP_CHANGE_NOT_MATCH_HAP;
110         }
111     }
112     if (type == WorkCondition::Type::STANDBY && value) {
113         isStandby_ = value->boolVal;
114     }
115     if (isStandby_ && !DelayedSingleton<WorkSchedulerService>::GetInstance()->CheckStandbyApplyInfo(bundleName_)) {
116         return E_GROUP_CHANGE_NOT_MATCH_HAP;
117     }
118     if (IsReady()) {
119         MarkStatus(Status::CONDITION_READY);
120     }
121     return ERR_OK;
122 }
123 
MakeWorkId(int32_t workId, int32_t uid)124 string WorkStatus::MakeWorkId(int32_t workId, int32_t uid)
125 {
126     return string("u") + to_string(uid) + "_" + to_string(workId);
127 }
128 
MarkTimeout()129 void WorkStatus::MarkTimeout()
130 {
131     lastTimeout_ = true;
132 }
133 
MarkStatus(Status status)134 void WorkStatus::MarkStatus(Status status)
135 {
136     currentStatus_ = status;
137 }
138 
MarkRound()139 void WorkStatus::MarkRound() {}
140 
UpdateTimerIfNeed()141 void WorkStatus::UpdateTimerIfNeed()
142 {
143     std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
144     if (conditionMap_.count(WorkCondition::Type::TIMER) > 0) {
145         baseTime_ = getCurrentTime();
146         if (conditionMap_.at(WorkCondition::Type::TIMER)->boolVal) {
147             workInfo_->RequestBaseTime(baseTime_);
148             DelayedSingleton<WorkSchedulerService>::GetInstance()->RefreshPersistedWorks();
149             return;
150         }
151         int32_t cycleLeft = conditionMap_.at(WorkCondition::Type::TIMER)->intVal;
152         conditionMap_.at(WorkCondition::Type::TIMER)->intVal = cycleLeft - 1;
153         workInfo_->RequestBaseTimeAndCycle(baseTime_, cycleLeft - 1);
154         DelayedSingleton<WorkSchedulerService>::GetInstance()->RefreshPersistedWorks();
155     }
156 }
157 
NeedRemove()158 bool WorkStatus::NeedRemove()
159 {
160     std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
161     if (conditionMap_.count(WorkCondition::Type::TIMER) <= 0) {
162         return true;
163     }
164     if (conditionMap_.at(WorkCondition::Type::TIMER)->boolVal) {
165         return false;
166     }
167     if (conditionMap_.at(WorkCondition::Type::TIMER)->intVal <= 0) {
168         return true;
169     }
170     return false;
171 }
172 
IsSameUser()173 bool WorkStatus::IsSameUser()
174 {
175     if (userId_ > 0 && !WorkSchedUtils::IsIdActive(userId_)) {
176         return false;
177     }
178     return true;
179 }
180 
IsUriKeySwitchOn()181 bool WorkStatus::IsUriKeySwitchOn()
182 {
183     if (!workInfo_->IsPreinstalled()) {
184         return true;
185     }
186     if (workInfo_->GetUriKey().empty()) {
187         WS_HILOGE("key is empty %{public}s", workId_.c_str());
188         return false;
189     }
190     string key = workInfo_->GetUriKey();
191     string value;
192     (void)WorkDatashareHelper::GetInstance().GetStringValue(key, value);
193     if (value == SWITCH_ON) {
194         return true;
195     }
196     WS_HILOGE("workid %{public}s key %{public}s, value is 0", workId_.c_str(), key.c_str());
197     return false;
198 }
199 
IsReady()200 bool WorkStatus::IsReady()
201 {
202     WS_HILOGD("IsReady");
203     if (!IsSameUser()) {
204         WS_HILOGI("Not same user. WorkId:%{public}s", workId_.c_str());
205         return false;
206     }
207     if (IsRunning()) {
208         WS_HILOGD("Work is running");
209         return false;
210     }
211     auto workConditionMap = workInfo_->GetConditionMap();
212     std::lock_guard<ffrt::mutex> lock(s_uid_last_time_mutex);
213     for (auto it : *workConditionMap) {
214         if (conditionMap_.count(it.first) <= 0) {
215             return false;
216         }
217         if (!IsBatteryAndNetworkReady(it.first) || !IsStorageAndTimerReady(it.first) ||
218             !IsChargerReady(it.first) || !IsNapReady(it.first)) {
219             return false;
220         }
221     }
222     if (!IsUriKeySwitchOn()) {
223         return false;
224     }
225     if (DelayedSingleton<WorkSchedulerService>::GetInstance()->CheckEffiResApplyInfo(uid_)) {
226         return true;
227     }
228     if (!debugMode && ((!callbackFlag_ && !SetMinInterval()) || minInterval_ == -1)) {
229         WS_HILOGE("Work can't ready due to false group, forbidden group or unused group, bundleName:%{public}s, "
230             "minInterval:%{public}" PRId64 ", workId:%{public}s", bundleName_.c_str(), minInterval_, workId_.c_str());
231         return false;
232     }
233 
234     auto itMap = s_uid_last_time_map.find(uid_);
235     if (itMap == s_uid_last_time_map.end()) {
236         WS_HILOGI("bundleName:%{public}s, uid:%{public}d", bundleName_.c_str(), uid_);
237         return true;
238     }
239     time_t lastTime = s_uid_last_time_map[uid_];
240     double del = difftime(getOppositeTime(), lastTime);
241     WS_HILOGD("CbFlag:%{public}d, minInterval:%{public}" PRId64 ", del:%{public}f", callbackFlag_, minInterval_, del);
242     if (del < minInterval_) {
243         needRetrigger_ = true;
244         timeRetrigger_ = int(minInterval_ - del + ONE_SECOND);
245         return false;
246     }
247     WS_HILOGI("bundleName:%{public}s, abilityName:%{public}s, workId:%{public}s, "
248         "callbackFlag:%{public}d, minInterval:%{public}" PRId64 ", del = %{public}f",
249         bundleName_.c_str(), abilityName_.c_str(), workId_.c_str(), callbackFlag_, minInterval_, del);
250     return true;
251 }
252 
IsBatteryAndNetworkReady(WorkCondition::Type type)253 bool WorkStatus::IsBatteryAndNetworkReady(WorkCondition::Type type)
254 {
255     auto workConditionMap = workInfo_->GetConditionMap();
256     switch (type) {
257         case WorkCondition::Type::NETWORK: {
258             if (conditionMap_.at(type)->enumVal == WorkCondition::Network::NETWORK_UNKNOWN) {
259                 return false;
260             }
261             if (workConditionMap->at(type)->enumVal != WorkCondition::Network::NETWORK_TYPE_ANY &&
262                 workConditionMap->at(type)->enumVal != conditionMap_.at(type)->enumVal) {
263                 return false;
264             }
265             break;
266         }
267         case WorkCondition::Type::BATTERY_STATUS: {
268             int32_t batteryReq = workConditionMap->at(type)->enumVal;
269             if (batteryReq != WorkCondition::BatteryStatus::BATTERY_STATUS_LOW_OR_OKAY &&
270                 batteryReq != conditionMap_.at(type)->enumVal) {
271                 return false;
272             }
273             break;
274         }
275         case WorkCondition::Type::BATTERY_LEVEL: {
276             if (workConditionMap->at(type)->intVal > conditionMap_.at(type)->intVal) {
277                 return false;
278             }
279             break;
280         }
281         default:
282             break;
283     }
284     return true;
285 }
286 
IsChargerReady(WorkCondition::Type type)287 bool WorkStatus::IsChargerReady(WorkCondition::Type type)
288 {
289     if (type != WorkCondition::Type::CHARGER) {
290         return true;
291     }
292     auto conditionSet = workInfo_->GetConditionMap()->at(WorkCondition::Type::CHARGER);
293     auto conditionCurrent = conditionMap_.at(WorkCondition::Type::CHARGER);
294     if (conditionSet->boolVal != conditionCurrent->boolVal) {
295         return false;
296     }
297     if (conditionSet->boolVal) {
298         if (conditionCurrent->enumVal != conditionSet->enumVal && conditionSet->enumVal !=
299             static_cast<int32_t>(WorkCondition::Charger::CHARGING_PLUGGED_ANY)) {
300             return false;
301         }
302     } else {
303         if (conditionCurrent->enumVal != static_cast<int32_t>(WorkCondition::Charger::CHARGING_UNPLUGGED)) {
304             return false;
305         }
306     }
307     return true;
308 }
309 
310 
IsStorageAndTimerReady(WorkCondition::Type type)311 bool WorkStatus::IsStorageAndTimerReady(WorkCondition::Type type)
312 {
313     auto workConditionMap = workInfo_->GetConditionMap();
314     switch (type) {
315         case WorkCondition::Type::STORAGE: {
316             if (workConditionMap->at(type)->enumVal != WorkCondition::Storage::STORAGE_LEVEL_LOW_OR_OKAY &&
317                 workConditionMap->at(type)->enumVal != conditionMap_.at(type)->enumVal) {
318                 return false;
319             }
320             break;
321         }
322         case WorkCondition::Type::TIMER: {
323             uint32_t intervalTime = workConditionMap->at(WorkCondition::Type::TIMER)->uintVal;
324             time_t lastTime;
325             if (s_uid_last_time_map.find(uid_) == s_uid_last_time_map.end()) {
326                 lastTime = 0;
327             } else {
328                 lastTime = s_uid_last_time_map[uid_];
329             }
330             double currentdel = difftime(getCurrentTime(), baseTime_) * ONE_SECOND;
331             double oppositedel = difftime(getOppositeTime(), lastTime);
332             double del = currentdel > oppositedel ? currentdel : oppositedel;
333             WS_HILOGD("del time: %{public}lf, intervalTime: %{public}u", del, intervalTime);
334             WS_HILOGD("currentdel time: %{public}lf, oppositedel time: %{public}lf", currentdel, oppositedel);
335             if (del < intervalTime) {
336                 return false;
337             }
338             break;
339         }
340         default:
341             break;
342     }
343     return true;
344 }
345 
IsNapReady(WorkCondition::Type type)346 bool WorkStatus::IsNapReady(WorkCondition::Type type)
347 {
348     if (type != WorkCondition::Type::DEEP_IDLE) {
349         return true;
350     }
351     auto conditionSet = workInfo_->GetConditionMap()->at(WorkCondition::Type::DEEP_IDLE);
352     auto conditionCurrent = conditionMap_.at(WorkCondition::Type::DEEP_IDLE);
353     if (conditionSet->boolVal != conditionCurrent->boolVal) {
354         return false;
355     }
356     return true;
357 }
358 
SetMinInterval()359 bool WorkStatus::SetMinInterval()
360 {
361 #ifdef DEVICE_USAGE_STATISTICS_ENABLE
362     int32_t group = 0;
363     if (workInfo_->IsCallBySystemApp()) {
364         WS_HILOGD("Is system app, default group is active.");
365         return SetMinIntervalByGroup(ACTIVE_GROUP);
366     }
367     int32_t errCode = DeviceUsageStats::BundleActiveClient::GetInstance().QueryAppGroup(group, bundleName_, userId_);
368     if (errCode != ERR_OK) {
369         WS_HILOGE("Query package group failed. userId = %{public}d, bundleName = %{public}s",
370             userId_, bundleName_.c_str());
371         group = ACTIVE_GROUP;
372     }
373 #else
374     int32_t group = ACTIVE_GROUP;
375 #endif
376     return SetMinIntervalByGroup(group);
377 }
378 
SetMinIntervalByGroup(int32_t group)379 bool WorkStatus::SetMinIntervalByGroup(int32_t group)
380 {
381     callbackFlag_ = true;
382 #ifdef DEVICE_USAGE_STATISTICS_ENABLE
383     auto itMap = DeviceUsageStats::DeviceUsageStatsGroupMap::groupIntervalMap_.find(group);
384     if (itMap != DeviceUsageStats::DeviceUsageStatsGroupMap::groupIntervalMap_.end()) {
385         minInterval_ = DeviceUsageStats::DeviceUsageStatsGroupMap::groupIntervalMap_[group];
386     } else {
387         WS_HILOGE("Query package group interval failed. group:%{public}d, bundleName:%{public}s",
388             group, bundleName_.c_str());
389         minInterval_ = -1;
390     }
391 #else
392     minInterval_ = MIN_INTERVAL_DEFAULT;
393 #endif
394     WS_HILOGD("Set min interval to %{public}" PRId64 " by group %{public}d", minInterval_, group);
395     return true;
396 }
397 
SetMinIntervalByDump(int64_t interval)398 void WorkStatus::SetMinIntervalByDump(int64_t interval)
399 {
400     WS_HILOGD("Set min interval by dump to %{public}" PRId64 "", interval);
401     debugMode = interval == 0 ? false : true;
402     minInterval_ = interval == 0 ? minInterval_ : interval;
403 }
404 
GetMinInterval()405 int64_t WorkStatus::GetMinInterval()
406 {
407     return minInterval_;
408 }
409 
UpdateUidLastTimeMap()410 void WorkStatus::UpdateUidLastTimeMap()
411 {
412     std::lock_guard<ffrt::mutex> lock(s_uid_last_time_mutex);
413     time_t lastTime = getOppositeTime();
414     s_uid_last_time_map[uid_] = lastTime;
415 }
416 
ClearUidLastTimeMap(int32_t uid)417 void WorkStatus::ClearUidLastTimeMap(int32_t uid)
418 {
419     std::lock_guard<ffrt::mutex> lock(s_uid_last_time_mutex);
420     s_uid_last_time_map.erase(uid);
421 }
422 
IsRunning()423 bool WorkStatus::IsRunning()
424 {
425     return currentStatus_ == RUNNING;
426 }
427 
IsPaused()428 bool WorkStatus::IsPaused()
429 {
430     return paused_;
431 }
432 
IsReadyStatus()433 bool WorkStatus::IsReadyStatus()
434 {
435     return currentStatus_ == CONDITION_READY;
436 }
437 
IsRemoved()438 bool WorkStatus::IsRemoved()
439 {
440     return currentStatus_ == REMOVED;
441 }
442 
IsLastWorkTimeout()443 bool WorkStatus::IsLastWorkTimeout()
444 {
445     return lastTimeout_;
446 }
447 
IsRepeating()448 bool WorkStatus::IsRepeating()
449 {
450     std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
451     if (conditionMap_.count(WorkCondition::Type::TIMER) <= 0) {
452         return false;
453     }
454     if (conditionMap_.at(WorkCondition::Type::TIMER)->boolVal) {
455         return true;
456     } else {
457         return conditionMap_.at(WorkCondition::Type::TIMER)->intVal > 0;
458     }
459 }
460 
GetStatus()461 WorkStatus::Status WorkStatus::GetStatus()
462 {
463     return currentStatus_;
464 }
465 
GetPriority()466 int WorkStatus::GetPriority()
467 {
468     if ((OHOS::system::GetIntParameter("const.debuggable", 0) == 1) &&
469         (bundleName_ == "com.huawei.hmos.hiviewx")) {
470         return HIGH_PRIORITY;
471     }
472     return DEFAULT_PRIORITY;
473 }
474 
Dump(string& result)475 void WorkStatus::Dump(string& result)
476 {
477     result.append("{\n");
478     result.append(string("\"workId\":") + workId_ + ",\n");
479     result.append(string("\"bundleName\":") + bundleName_ + ",\n");
480     result.append(string("\"status\":") + to_string(currentStatus_) + ",\n");
481     result.append(string("\"paused\":") + (paused_ ? "true" : "false") + ",\n");
482     result.append(string("\"priority\":") + to_string(priority_) + ",\n");
483     result.append(string("\"conditionMap\":{\n"));
484     std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
485     if (conditionMap_.count(WorkCondition::Type::NETWORK) > 0) {
486         result.append(string("\"networkType\":") +
487             to_string(conditionMap_.at(WorkCondition::Type::NETWORK)->enumVal) + ",\n");
488     }
489     if (conditionMap_.count(WorkCondition::Type::CHARGER) > 0) {
490         result.append(string("\"isCharging\":") +
491             (conditionMap_.at(WorkCondition::Type::CHARGER)->boolVal ? "true" : "false") + ",\n");
492         result.append(string("\"chargerType\":") +
493             to_string(conditionMap_.at(WorkCondition::Type::CHARGER)->enumVal) + ",\n");
494     }
495     if (conditionMap_.count(WorkCondition::Type::BATTERY_LEVEL) > 0) {
496         result.append(string("\"batteryLevel\":") +
497             to_string(conditionMap_.at(WorkCondition::Type::BATTERY_LEVEL)->intVal) + ",\n");
498     }
499     if (conditionMap_.count(WorkCondition::Type::BATTERY_STATUS) > 0) {
500         result.append(string("\"batteryStatus\":") +
501             to_string(conditionMap_.at(WorkCondition::Type::BATTERY_STATUS)->enumVal) + ",\n");
502     }
503     if (conditionMap_.count(WorkCondition::Type::STORAGE) > 0) {
504         result.append(string("\"storageLevel\":") +
505             to_string(conditionMap_.at(WorkCondition::Type::STORAGE)->enumVal) + ",\n");
506     }
507     if (conditionMap_.count(WorkCondition::Type::TIMER) > 0) {
508         result.append(string("\"baseTime\":") + to_string(baseTime_) + ",\n");
509         if (conditionMap_.at(WorkCondition::Type::TIMER)->boolVal) {
510             result.append(string("\"isRepeat\": true,\n"));
511         } else {
512             result.append(string("\"cycleLeft\":") +
513                 to_string(conditionMap_.at(WorkCondition::Type::TIMER)->intVal) + ",\n");
514         }
515     }
516     if (conditionMap_.count(WorkCondition::Type::DEEP_IDLE) > 0) {
517         result.append(string("\"isDeepIdle\":") +
518             to_string(conditionMap_.at(WorkCondition::Type::DEEP_IDLE)->boolVal) + ",\n");
519     }
520     result.append("},\n\"workInfo\":\n");
521     workInfo_->Dump(result);
522     result.append("}\n");
523     result.append("\n");
524 }
525 } // namespace WorkScheduler
526 } // namespace OHOS