• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "work_sched_data_manager.h"
31 #include "work_sched_config.h"
32 
33 using namespace std;
34 
35 namespace OHOS {
36 namespace WorkScheduler {
37 static const double ONE_SECOND = 1000.0;
38 static bool g_groupDebugMode = false;
39 static const int64_t MIN_INTERVAL_DEFAULT = 2 * 60 * 60 * 1000;
40 std::map<int32_t, time_t> WorkStatus::s_uid_last_time_map;
41 const int32_t DEFAULT_PRIORITY = 10000;
42 const int32_t HIGH_PRIORITY = 0;
43 const int32_t ACTIVE_GROUP = 10;
44 const string SWITCH_ON = "1";
45 ffrt::mutex WorkStatus::s_uid_last_time_mutex;
46 
getCurrentTime()47 time_t getCurrentTime()
48 {
49     time_t result;
50     time(&result);
51     return result;
52 }
53 
getOppositeTime()54 time_t getOppositeTime()
55 {
56     time_t result;
57     sptr<MiscServices::TimeServiceClient> timer = MiscServices::TimeServiceClient::GetInstance();
58     result = static_cast<time_t>(timer->GetBootTimeMs());
59     return result;
60 }
61 
WorkStatus(WorkInfo & workInfo,int32_t uid)62 WorkStatus::WorkStatus(WorkInfo &workInfo, int32_t uid)
63 {
64     this->workInfo_ = make_shared<WorkInfo>(workInfo);
65     this->workId_ = MakeWorkId(workInfo.GetWorkId(), uid);
66     this->bundleName_ = workInfo.GetBundleName();
67     this->abilityName_ = workInfo.GetAbilityName();
68     this->baseTime_ = workInfo.GetBaseTime();
69     this->uid_ = uid;
70     this->userId_ = WorkSchedUtils::GetUserIdByUid(uid);
71     if (workInfo.GetConditionMap()->count(WorkCondition::Type::TIMER) > 0) {
72         auto workTimerCondition = workInfo.GetConditionMap()->at(WorkCondition::Type::TIMER);
73         shared_ptr<Condition> timeCondition = make_shared<Condition>();
74         timeCondition->uintVal = workTimerCondition->uintVal;
75         timeCondition->boolVal = workTimerCondition->boolVal;
76         if (!workTimerCondition->boolVal) {
77             timeCondition->intVal = workTimerCondition->intVal;
78         }
79         std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
80         conditionMap_.emplace(WorkCondition::Type::TIMER, timeCondition);
81     }
82     this->persisted_ = workInfo.IsPersisted();
83     this->priority_ = GetPriority();
84     this->currentStatus_ = WAIT_CONDITION;
85     this->minInterval_ = MIN_INTERVAL_DEFAULT;
86     this->groupChanged_ = false;
87 }
88 
~WorkStatus()89 WorkStatus::~WorkStatus() {}
90 
OnConditionChanged(WorkCondition::Type & type,shared_ptr<Condition> value)91 int32_t WorkStatus::OnConditionChanged(WorkCondition::Type &type, shared_ptr<Condition> value)
92 {
93     WS_HILOGD("Work status condition changed.");
94     if (workInfo_->GetConditionMap()->count(type) > 0
95         && type != WorkCondition::Type::TIMER
96         && type != WorkCondition::Type::GROUP) {
97         std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
98         if (conditionMap_.count(type) > 0) {
99             conditionMap_.at(type) = value;
100         } else {
101             conditionMap_.emplace(type, value);
102         }
103     }
104     groupChanged_ = false;
105     if (type == WorkCondition::Type::GROUP && value && value->boolVal) {
106         WS_HILOGD("Group changed, bundleName: %{public}s.", value->strVal.c_str());
107         groupChanged_ = true;
108         if (value->intVal == userId_ && value->strVal == bundleName_) {
109             SetMinIntervalByGroup(value->enumVal);
110         } else {
111             return E_GROUP_CHANGE_NOT_MATCH_HAP;
112         }
113     }
114     auto dataManager = DelayedSingleton<DataManager>::GetInstance();
115     if (dataManager->GetDeviceSleep() && !dataManager->IsInDeviceStandyWhitelist(bundleName_)) {
116         WS_HILOGI("Standby mode, Work status:%{public}s not standby exempted.", bundleName_.c_str());
117         return E_GROUP_CHANGE_NOT_MATCH_HAP;
118     }
119     if (IsReady()) {
120         MarkStatus(Status::CONDITION_READY);
121     }
122     return ERR_OK;
123 }
124 
MakeWorkId(int32_t workId,int32_t uid)125 string WorkStatus::MakeWorkId(int32_t workId, int32_t uid)
126 {
127     return string("u") + to_string(uid) + "_" + to_string(workId);
128 }
129 
MarkTimeout()130 void WorkStatus::MarkTimeout()
131 {
132     lastTimeout_ = true;
133 }
134 
MarkStatus(Status status)135 void WorkStatus::MarkStatus(Status status)
136 {
137     currentStatus_ = status;
138 }
139 
MarkRound()140 void WorkStatus::MarkRound() {}
141 
UpdateTimerIfNeed()142 void WorkStatus::UpdateTimerIfNeed()
143 {
144     std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
145     if (conditionMap_.count(WorkCondition::Type::TIMER) > 0) {
146         baseTime_ = getCurrentTime();
147         if (conditionMap_.at(WorkCondition::Type::TIMER)->boolVal) {
148             workInfo_->RequestBaseTime(baseTime_);
149             DelayedSingleton<WorkSchedulerService>::GetInstance()->RefreshPersistedWorks();
150             return;
151         }
152         int32_t cycleLeft = conditionMap_.at(WorkCondition::Type::TIMER)->intVal;
153         conditionMap_.at(WorkCondition::Type::TIMER)->intVal = cycleLeft - 1;
154         workInfo_->RequestBaseTimeAndCycle(baseTime_, cycleLeft - 1);
155         DelayedSingleton<WorkSchedulerService>::GetInstance()->RefreshPersistedWorks();
156     }
157 }
158 
NeedRemove()159 bool WorkStatus::NeedRemove()
160 {
161     std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
162     if (conditionMap_.count(WorkCondition::Type::TIMER) <= 0) {
163         return true;
164     }
165     if (conditionMap_.at(WorkCondition::Type::TIMER)->boolVal) {
166         return false;
167     }
168     if (conditionMap_.at(WorkCondition::Type::TIMER)->intVal <= 0) {
169         return true;
170     }
171     return false;
172 }
173 
IsSameUser()174 bool WorkStatus::IsSameUser()
175 {
176     if (userId_ > 0 && !WorkSchedUtils::IsIdActive(userId_)) {
177         return false;
178     }
179     return true;
180 }
181 
IsUriKeySwitchOn()182 bool WorkStatus::IsUriKeySwitchOn()
183 {
184     if (!workInfo_->IsPreinstalled()) {
185         return true;
186     }
187     if (workInfo_->GetUriKey().empty()) {
188         WS_HILOGE("key is empty %{public}s", workId_.c_str());
189         return false;
190     }
191     string key = workInfo_->GetUriKey();
192     string value;
193     (void)WorkDatashareHelper::GetInstance().GetStringValue(key, value);
194     if (value == SWITCH_ON) {
195         return true;
196     }
197     WS_HILOGE("workid %{public}s key %{public}s, value is 0", workId_.c_str(), key.c_str());
198     return false;
199 }
200 
IsReady()201 bool WorkStatus::IsReady()
202 {
203     WS_HILOGD("IsReady");
204     if (!IsSameUser()) {
205         WS_HILOGI("Not same user. WorkId:%{public}s", workId_.c_str());
206         return false;
207     }
208     if (IsRunning()) {
209         WS_HILOGD("Work is running");
210         return false;
211     }
212     auto workConditionMap = workInfo_->GetConditionMap();
213     std::lock_guard<ffrt::mutex> lock(s_uid_last_time_mutex);
214     for (auto it : *workConditionMap) {
215         if (conditionMap_.count(it.first) <= 0) {
216             return false;
217         }
218         if (!IsBatteryAndNetworkReady(it.first) || !IsStorageAndTimerReady(it.first) ||
219             !IsChargerReady(it.first) || !IsNapReady(it.first)) {
220             return false;
221         }
222     }
223     if (!IsUriKeySwitchOn()) {
224         return false;
225     }
226     if (DelayedSingleton<WorkSchedulerService>::GetInstance()->CheckEffiResApplyInfo(uid_)) {
227         return true;
228     }
229     if (!g_groupDebugMode && ((!groupChanged_ && !SetMinInterval()) || minInterval_ == -1)) {
230         WS_HILOGE("Work can't ready due to false group, forbidden group or unused group, bundleName:%{public}s, "
231             "minInterval:%{public}" PRId64 ", workId:%{public}s", bundleName_.c_str(), minInterval_, workId_.c_str());
232         return false;
233     }
234 
235     if (s_uid_last_time_map.find(uid_) == s_uid_last_time_map.end()) {
236         WS_HILOGI("First trigger, bundleName:%{public}s, uid:%{public}d", bundleName_.c_str(), uid_);
237         return true;
238     }
239     double del = difftime(getOppositeTime(), s_uid_last_time_map[uid_]);
240     if (del < minInterval_) {
241         WS_HILOGI("Condition not ready, bundleName:%{public}s, workId:%{public}s, "
242             "minInterval:%{public}" PRId64 ", del:%{public}f", bundleName_.c_str(), workId_.c_str(), minInterval_, del);
243         needRetrigger_ = true;
244         timeRetrigger_ = int(minInterval_ - del + ONE_SECOND);
245         return false;
246     }
247     WS_HILOGI("Condition Ready, bundleName:%{public}s, abilityName:%{public}s, workId:%{public}s, "
248         "groupChanged:%{public}d, minInterval:%{public}" PRId64 ", del = %{public}f",
249         bundleName_.c_str(), abilityName_.c_str(), workId_.c_str(), groupChanged_, 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("system app %{public}s, default group is active.", bundleName_.c_str());
365         return SetMinIntervalByGroup(ACTIVE_GROUP);
366     }
367     bool res = DelayedSingleton<DataManager>::GetInstance()->FindGroup(bundleName_, userId_, group);
368     if (!res) {
369         WS_HILOGI("no cache find, bundleName:%{public}s", bundleName_.c_str());
370         auto errCode = DeviceUsageStats::BundleActiveClient::GetInstance().QueryAppGroup(group, bundleName_, userId_);
371         if (errCode != ERR_OK) {
372             WS_HILOGE("query package group failed. userId = %{public}d, bundleName = %{public}s",
373                 userId_, bundleName_.c_str());
374             group = ACTIVE_GROUP;
375         }
376         DelayedSingleton<DataManager>::GetInstance()->AddGroup(bundleName_, userId_, group);
377     }
378 #else
379     int32_t group = ACTIVE_GROUP;
380 #endif
381     return SetMinIntervalByGroup(group);
382 }
383 
SetMinIntervalByGroup(int32_t group)384 bool WorkStatus::SetMinIntervalByGroup(int32_t group)
385 {
386     groupChanged_ = true;
387 
388 #ifdef DEVICE_USAGE_STATISTICS_ENABLE
389     int32_t newGroup = group;
390     if (DelayedSingleton<WorkSchedulerConfig>::GetInstance()->IsInActiveGroupWhitelist(bundleName_) &&
391         group > DeviceUsageStats::DeviceUsageStatsGroupConst::ACTIVE_GROUP_FIXED) {
392         newGroup = DeviceUsageStats::DeviceUsageStatsGroupConst::ACTIVE_GROUP_FIXED;
393     }
394     auto itMap = DeviceUsageStats::DeviceUsageStatsGroupMap::groupIntervalMap_.find(newGroup);
395     if (itMap != DeviceUsageStats::DeviceUsageStatsGroupMap::groupIntervalMap_.end()) {
396         minInterval_ = DeviceUsageStats::DeviceUsageStatsGroupMap::groupIntervalMap_[newGroup];
397     } else {
398         WS_HILOGE("query package group interval failed. group:%{public}d, bundleName:%{public}s",
399             newGroup, bundleName_.c_str());
400         minInterval_ = -1;
401     }
402 #else
403     minInterval_ = MIN_INTERVAL_DEFAULT;
404 #endif
405     WS_HILOGD("set min interval to %{public}" PRId64 " by group %{public}d", minInterval_, group);
406     return true;
407 }
408 
SetMinIntervalByDump(int64_t interval)409 void WorkStatus::SetMinIntervalByDump(int64_t interval)
410 {
411     WS_HILOGD("set min interval by dump to %{public}" PRId64 "", interval);
412     g_groupDebugMode = interval == 0 ? false : true;
413     minInterval_ = interval == 0 ? minInterval_ : interval;
414 }
415 
GetMinInterval()416 int64_t WorkStatus::GetMinInterval()
417 {
418     return minInterval_;
419 }
420 
UpdateUidLastTimeMap()421 void WorkStatus::UpdateUidLastTimeMap()
422 {
423     std::lock_guard<ffrt::mutex> lock(s_uid_last_time_mutex);
424     time_t lastTime = getOppositeTime();
425     s_uid_last_time_map[uid_] = lastTime;
426 }
427 
ClearUidLastTimeMap(int32_t uid)428 void WorkStatus::ClearUidLastTimeMap(int32_t uid)
429 {
430     std::lock_guard<ffrt::mutex> lock(s_uid_last_time_mutex);
431     s_uid_last_time_map.erase(uid);
432 }
433 
IsRunning()434 bool WorkStatus::IsRunning()
435 {
436     return currentStatus_ == RUNNING;
437 }
438 
IsPaused()439 bool WorkStatus::IsPaused()
440 {
441     return paused_;
442 }
443 
IsReadyStatus()444 bool WorkStatus::IsReadyStatus()
445 {
446     return currentStatus_ == CONDITION_READY;
447 }
448 
IsRemoved()449 bool WorkStatus::IsRemoved()
450 {
451     return currentStatus_ == REMOVED;
452 }
453 
IsLastWorkTimeout()454 bool WorkStatus::IsLastWorkTimeout()
455 {
456     return lastTimeout_;
457 }
458 
IsRepeating()459 bool WorkStatus::IsRepeating()
460 {
461     std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
462     if (conditionMap_.count(WorkCondition::Type::TIMER) <= 0) {
463         return false;
464     }
465     if (conditionMap_.at(WorkCondition::Type::TIMER)->boolVal) {
466         return true;
467     } else {
468         return conditionMap_.at(WorkCondition::Type::TIMER)->intVal > 0;
469     }
470 }
471 
GetStatus()472 WorkStatus::Status WorkStatus::GetStatus()
473 {
474     return currentStatus_;
475 }
476 
GetPriority()477 int WorkStatus::GetPriority()
478 {
479     if ((OHOS::system::GetIntParameter("const.debuggable", 0) == 1) &&
480         (bundleName_ == "com.huawei.hmos.hiviewx")) {
481         return HIGH_PRIORITY;
482     }
483     return DEFAULT_PRIORITY;
484 }
485 
Dump(string & result)486 void WorkStatus::Dump(string& result)
487 {
488     result.append("{\n");
489     result.append(string("\"workId\":") + workId_ + ",\n");
490     result.append(string("\"bundleName\":") + bundleName_ + ",\n");
491     result.append(string("\"status\":") + to_string(currentStatus_) + ",\n");
492     result.append(string("\"paused\":") + (paused_ ? "true" : "false") + ",\n");
493     result.append(string("\"priority\":") + to_string(priority_) + ",\n");
494     result.append(string("\"conditionMap\":{\n"));
495     std::lock_guard<ffrt::mutex> lock(conditionMapMutex_);
496     if (conditionMap_.count(WorkCondition::Type::NETWORK) > 0) {
497         result.append(string("\"networkType\":") +
498             to_string(conditionMap_.at(WorkCondition::Type::NETWORK)->enumVal) + ",\n");
499     }
500     if (conditionMap_.count(WorkCondition::Type::CHARGER) > 0) {
501         result.append(string("\"isCharging\":") +
502             (conditionMap_.at(WorkCondition::Type::CHARGER)->boolVal ? "true" : "false") + ",\n");
503         result.append(string("\"chargerType\":") +
504             to_string(conditionMap_.at(WorkCondition::Type::CHARGER)->enumVal) + ",\n");
505     }
506     if (conditionMap_.count(WorkCondition::Type::BATTERY_LEVEL) > 0) {
507         result.append(string("\"batteryLevel\":") +
508             to_string(conditionMap_.at(WorkCondition::Type::BATTERY_LEVEL)->intVal) + ",\n");
509     }
510     if (conditionMap_.count(WorkCondition::Type::BATTERY_STATUS) > 0) {
511         result.append(string("\"batteryStatus\":") +
512             to_string(conditionMap_.at(WorkCondition::Type::BATTERY_STATUS)->enumVal) + ",\n");
513     }
514     if (conditionMap_.count(WorkCondition::Type::STORAGE) > 0) {
515         result.append(string("\"storageLevel\":") +
516             to_string(conditionMap_.at(WorkCondition::Type::STORAGE)->enumVal) + ",\n");
517     }
518     if (conditionMap_.count(WorkCondition::Type::TIMER) > 0) {
519         result.append(string("\"baseTime\":") + to_string(baseTime_) + ",\n");
520         if (conditionMap_.at(WorkCondition::Type::TIMER)->boolVal) {
521             result.append(string("\"isRepeat\": true,\n"));
522         } else {
523             result.append(string("\"cycleLeft\":") +
524                 to_string(conditionMap_.at(WorkCondition::Type::TIMER)->intVal) + ",\n");
525         }
526     }
527     if (conditionMap_.count(WorkCondition::Type::DEEP_IDLE) > 0) {
528         result.append(string("\"isDeepIdle\":") +
529             to_string(conditionMap_.at(WorkCondition::Type::DEEP_IDLE)->boolVal) + ",\n");
530     }
531     result.append("},\n\"workInfo\":\n");
532     workInfo_->Dump(result);
533     result.append("}\n");
534     result.append("\n");
535 }
536 } // namespace WorkScheduler
537 } // namespace OHOS