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