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