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