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