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 #include <hisysevent.h>
16 #include <ipc_skeleton.h>
17 
18 #include "work_queue_manager.h"
19 #include "work_scheduler_service.h"
20 #include "work_sched_hilog.h"
21 #include "work_sched_utils.h"
22 
23 using namespace std;
24 
25 namespace OHOS {
26 namespace WorkScheduler {
27 const uint32_t TIME_CYCLE = 20 * 60 * 1000; // 20min
28 static int32_t g_timeRetrigger = INT32_MAX;
29 
WorkQueueManager(const std::shared_ptr<WorkSchedulerService>& wss)30 WorkQueueManager::WorkQueueManager(const std::shared_ptr<WorkSchedulerService>& wss) : wss_(wss)
31 {
32     timeCycle_ = TIME_CYCLE;
33 }
34 
Init()35 bool WorkQueueManager::Init()
36 {
37     return true;
38 }
39 
AddListener(WorkCondition::Type type, shared_ptr<IConditionListener> listener)40 bool WorkQueueManager::AddListener(WorkCondition::Type type, shared_ptr<IConditionListener> listener)
41 {
42     std::lock_guard<ffrt::mutex> lock(mutex_);
43     if (listenerMap_.count(type) > 0) {
44         return false;
45     }
46     listenerMap_.emplace(type, listener);
47     return true;
48 }
49 
AddWork(shared_ptr<WorkStatus> workStatus)50 bool WorkQueueManager::AddWork(shared_ptr<WorkStatus> workStatus)
51 {
52     if (!workStatus || !workStatus->workInfo_ || !workStatus->workInfo_->GetConditionMap()) {
53         return false;
54     }
55     WS_HILOGD("workStatus ID: %{public}s", workStatus->workId_.c_str());
56     std::lock_guard<ffrt::mutex> lock(mutex_);
57     auto map = workStatus->workInfo_->GetConditionMap();
58     for (auto it : *map) {
59         if (queueMap_.count(it.first) == 0) {
60             queueMap_.emplace(it.first, make_shared<WorkQueue>());
61             if (it.first != WorkCondition::Type::BATTERY_LEVEL && listenerMap_.count(it.first) != 0) {
62                 listenerMap_.at(it.first)->Start();
63             }
64         }
65         queueMap_.at(it.first)->Push(workStatus);
66     }
67     if (WorkSchedUtils::IsSystemApp()) {
68         WS_HILOGI("Is system app, default group is active.");
69         workStatus->workInfo_->SetCallBySystemApp(true);
70     }
71     return true;
72 }
73 
RemoveWork(shared_ptr<WorkStatus> workStatus)74 bool WorkQueueManager::RemoveWork(shared_ptr<WorkStatus> workStatus)
75 {
76     std::lock_guard<ffrt::mutex> lock(mutex_);
77     WS_HILOGD("workStatus ID: %{public}s", workStatus->workId_.c_str());
78     auto map = workStatus->workInfo_->GetConditionMap();
79     for (auto it : *map) {
80         if (queueMap_.count(it.first) > 0) {
81             queueMap_.at(it.first)->Remove(workStatus);
82         }
83         if (queueMap_.count(it.first) == 0) {
84             listenerMap_.at(it.first)->Stop();
85         }
86     }
87     return true;
88 }
89 
CancelWork(shared_ptr<WorkStatus> workStatus)90 bool WorkQueueManager::CancelWork(shared_ptr<WorkStatus> workStatus)
91 {
92     std::lock_guard<ffrt::mutex> lock(mutex_);
93     WS_HILOGD("workStatus ID: %{public}s", workStatus->workId_.c_str());
94     for (auto it : queueMap_) {
95         it.second->CancelWork(workStatus);
96         if (queueMap_.count(it.first) == 0) {
97             if (it.first == WorkCondition::Type::BATTERY_LEVEL) {
98                 continue;
99             }
100             listenerMap_.at(it.first)->Stop();
101         }
102     }
103     // Notify work remove event to battery statistics
104     int32_t pid = IPCSkeleton::GetCallingPid();
105     HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::WORK_SCHEDULER,
106         "WORK_REMOVE", HiviewDFX::HiSysEvent::EventType::STATISTIC, "UID", workStatus->uid_,
107         "PID", pid, "NAME", workStatus->bundleName_, "WORKID", workStatus->workId_);
108     return true;
109 }
110 
GetReayQueue(WorkCondition::Type conditionType, shared_ptr<DetectorValue> conditionVal)111 vector<shared_ptr<WorkStatus>> WorkQueueManager::GetReayQueue(WorkCondition::Type conditionType,
112     shared_ptr<DetectorValue> conditionVal)
113 {
114     vector<shared_ptr<WorkStatus>> result;
115     std::lock_guard<ffrt::mutex> lock(mutex_);
116     if (conditionType != WorkCondition::Type::GROUP && queueMap_.count(conditionType) > 0) {
117         shared_ptr<WorkQueue> workQueue = queueMap_.at(conditionType);
118         result = workQueue->OnConditionChanged(conditionType, conditionVal);
119     }
120     if (conditionType == WorkCondition::Type::GROUP || conditionType == WorkCondition::Type::STANDBY) {
121         for (auto it : queueMap_) {
122             shared_ptr<WorkQueue> workQueue = it.second;
123             auto works = workQueue->OnConditionChanged(conditionType, conditionVal);
124             PushWork(works, result);
125         }
126     }
127     auto it = result.begin();
128     while (it != result.end()) {
129         if ((*it)->needRetrigger_) {
130             if (conditionType != WorkCondition::Type::TIMER
131                     && conditionType != WorkCondition::Type::GROUP) {
132                 WS_HILOGI("Need retrigger, start group listener, bundleName:%{public}s, workId:%{public}s",
133                     (*it)->bundleName_.c_str(), (*it)->workId_.c_str());
134                 SetTimeRetrigger((*it)->timeRetrigger_);
135                 listenerMap_.at(WorkCondition::Type::GROUP)->Start();
136             }
137             (*it)->needRetrigger_ = false;
138             (*it)->timeRetrigger_ = INT32_MAX;
139             it = result.erase(it);
140         } else {
141             ++it;
142         }
143     }
144     return result;
145 }
146 
PushWork(vector<shared_ptr<WorkStatus>> &works, vector<shared_ptr<WorkStatus>> &result)147 void WorkQueueManager::PushWork(vector<shared_ptr<WorkStatus>> &works, vector<shared_ptr<WorkStatus>> &result)
148 {
149     for (const auto &work : works) {
150         auto iter = std::find_if(result.begin(), result.end(),
151         [&](const auto &existingWork) {
152             return existingWork->workId_ == work->workId_;
153         });
154         if (iter != result.end()) {
155             WS_HILOGE("WorkId:%{public}s existing, bundleName:%{public}s",
156                 work->workId_.c_str(), work->bundleName_.c_str());
157             continue;
158         }
159         result.push_back(work);
160     }
161 }
162 
OnConditionChanged(WorkCondition::Type conditionType, shared_ptr<DetectorValue> conditionVal)163 void WorkQueueManager::OnConditionChanged(WorkCondition::Type conditionType,
164     shared_ptr<DetectorValue> conditionVal)
165 {
166     vector<shared_ptr<WorkStatus>> readyWorkVector = GetReayQueue(conditionType, conditionVal);
167     if (readyWorkVector.size() == 0) {
168         return;
169     }
170     for (auto it : readyWorkVector) {
171         it->MarkStatus(WorkStatus::Status::CONDITION_READY);
172     }
173     if (wss_.expired()) {
174         WS_HILOGE("wss_ expired");
175         return;
176     }
177     wss_.lock()->OnConditionReady(make_shared<vector<shared_ptr<WorkStatus>>>(readyWorkVector));
178 }
179 
StopAndClearWorks(list<shared_ptr<WorkStatus>> workList)180 bool WorkQueueManager::StopAndClearWorks(list<shared_ptr<WorkStatus>> workList)
181 {
182     for (auto &it : workList) {
183         CancelWork(it);
184     }
185     return true;
186 }
187 
Dump(string& result)188 void WorkQueueManager::Dump(string& result)
189 {
190     std::lock_guard<ffrt::mutex> lock(mutex_);
191     string conditionType[] = {"network", "charger", "battery_status", "battery_level",
192         "storage", "timer", "group", "deepIdle", "standby", "unknown"};
193     uint32_t size = sizeof(conditionType);
194     for (auto it : queueMap_) {
195         if (it.first < size) {
196             result.append(conditionType[it.first]);
197         } else {
198             result.append(conditionType[size - 1]);
199         }
200         result.append(" : ");
201         result.append("[");
202         string workIdStr;
203         it.second->GetWorkIdStr(workIdStr);
204         result.append(workIdStr);
205         result.append("]\n");
206     }
207 }
208 
SetTimeCycle(uint32_t time)209 void WorkQueueManager::SetTimeCycle(uint32_t time)
210 {
211     timeCycle_ = time;
212     listenerMap_.at(WorkCondition::Type::TIMER)->Stop();
213     listenerMap_.at(WorkCondition::Type::TIMER)->Start();
214 }
215 
GetTimeCycle()216 uint32_t WorkQueueManager::GetTimeCycle()
217 {
218     return timeCycle_;
219 }
220 
SetTimeRetrigger(int32_t time)221 void WorkQueueManager::SetTimeRetrigger(int32_t time)
222 {
223     g_timeRetrigger = time;
224 }
225 
GetTimeRetrigger()226 int32_t WorkQueueManager::GetTimeRetrigger()
227 {
228     return g_timeRetrigger;
229 }
230 
SetMinIntervalByDump(int64_t interval)231 void WorkQueueManager::SetMinIntervalByDump(int64_t interval)
232 {
233     for (auto it : queueMap_) {
234         it.second->SetMinIntervalByDump(interval);
235     }
236 }
237 } // namespace WorkScheduler
238 } // namespace OHOS