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 = 10 * 60 * 1000; // 10min
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)->Stop();
63                 listenerMap_.at(it.first)->Start();
64             }
65         }
66         queueMap_.at(it.first)->Push(workStatus);
67     }
68     if (WorkSchedUtils::IsSystemApp()) {
69         WS_HILOGI("Is system app, default group is active.");
70         workStatus->workInfo_->SetCallBySystemApp(true);
71     }
72     return true;
73 }
74 
RemoveWork(shared_ptr<WorkStatus> workStatus)75 bool WorkQueueManager::RemoveWork(shared_ptr<WorkStatus> workStatus)
76 {
77     std::lock_guard<ffrt::mutex> lock(mutex_);
78     WS_HILOGD("workStatus ID: %{public}s", workStatus->workId_.c_str());
79     auto map = workStatus->workInfo_->GetConditionMap();
80     for (auto it : *map) {
81         if (queueMap_.count(it.first) > 0) {
82             queueMap_.at(it.first)->Remove(workStatus);
83         }
84         if (queueMap_.count(it.first) == 0) {
85             listenerMap_.at(it.first)->Stop();
86         }
87     }
88     return true;
89 }
90 
CancelWork(shared_ptr<WorkStatus> workStatus)91 bool WorkQueueManager::CancelWork(shared_ptr<WorkStatus> workStatus)
92 {
93     std::lock_guard<ffrt::mutex> lock(mutex_);
94     WS_HILOGD("workStatus ID: %{public}s", workStatus->workId_.c_str());
95     for (auto it : queueMap_) {
96         it.second->CancelWork(workStatus);
97         if (queueMap_.count(it.first) == 0) {
98             if (it.first == WorkCondition::Type::BATTERY_LEVEL) {
99                 continue;
100             }
101             listenerMap_.at(it.first)->Stop();
102         }
103     }
104     // Notify work remove event to battery statistics
105     int32_t pid = IPCSkeleton::GetCallingPid();
106     HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::WORK_SCHEDULER,
107         "WORK_REMOVE", HiviewDFX::HiSysEvent::EventType::STATISTIC, "UID", workStatus->uid_,
108         "PID", pid, "NAME", workStatus->bundleName_, "WORKID", workStatus->workId_);
109     return true;
110 }
111 
GetReayQueue(WorkCondition::Type conditionType,shared_ptr<DetectorValue> conditionVal)112 vector<shared_ptr<WorkStatus>> WorkQueueManager::GetReayQueue(WorkCondition::Type conditionType,
113     shared_ptr<DetectorValue> conditionVal)
114 {
115     vector<shared_ptr<WorkStatus>> result;
116     std::lock_guard<ffrt::mutex> lock(mutex_);
117     if (conditionType != WorkCondition::Type::GROUP && queueMap_.count(conditionType) > 0) {
118         shared_ptr<WorkQueue> workQueue = queueMap_.at(conditionType);
119         result = workQueue->OnConditionChanged(conditionType, conditionVal);
120     }
121     if (conditionType == WorkCondition::Type::GROUP || conditionType == WorkCondition::Type::STANDBY) {
122         for (auto it : queueMap_) {
123             shared_ptr<WorkQueue> workQueue = it.second;
124             auto works = workQueue->OnConditionChanged(conditionType, conditionVal);
125             PushWork(works, result);
126         }
127     }
128     auto it = result.begin();
129     while (it != result.end()) {
130         if ((*it)->needRetrigger_) {
131             if (conditionType != WorkCondition::Type::TIMER
132                     && conditionType != WorkCondition::Type::GROUP) {
133                 WS_HILOGI("Need retrigger, start group listener, bundleName:%{public}s, workId:%{public}s",
134                     (*it)->bundleName_.c_str(), (*it)->workId_.c_str());
135                 SetTimeRetrigger((*it)->timeRetrigger_);
136                 listenerMap_.at(WorkCondition::Type::GROUP)->Start();
137             }
138             (*it)->needRetrigger_ = false;
139             (*it)->timeRetrigger_ = INT32_MAX;
140             it = result.erase(it);
141         } else {
142             ++it;
143         }
144     }
145     PrintWorkStatus(conditionType);
146     return result;
147 }
148 
PrintWorkStatus(WorkCondition::Type conditionType)149 void WorkQueueManager::PrintWorkStatus(WorkCondition::Type conditionType)
150 {
151     if (conditionType == WorkCondition::Type::GROUP || conditionType == WorkCondition::Type::STANDBY) {
152         PrintAllWorkStatus(conditionType);
153         return;
154     }
155     if (queueMap_.count(conditionType) > 0) {
156         shared_ptr<WorkQueue> workQueue = queueMap_.at(conditionType);
157         auto workList = workQueue->GetWorkList();
158         for (auto work : workList) {
159             work->ToString(conditionType);
160         }
161     }
162 }
163 
PrintAllWorkStatus(WorkCondition::Type conditionType)164 void WorkQueueManager::PrintAllWorkStatus(WorkCondition::Type conditionType)
165 {
166     std::set<std::string> allWorkIds;
167     for (auto it : queueMap_) {
168         shared_ptr<WorkQueue> workQueue = it.second;
169         auto workList = workQueue->GetWorkList();
170         for (auto work : workList) {
171             if (allWorkIds.count(work->workId_) != 0) {
172                 continue;
173             }
174             allWorkIds.insert(work->workId_);
175             work->ToString(conditionType);
176         }
177     }
178 }
179 
PushWork(vector<shared_ptr<WorkStatus>> & works,vector<shared_ptr<WorkStatus>> & result)180 void WorkQueueManager::PushWork(vector<shared_ptr<WorkStatus>> &works, vector<shared_ptr<WorkStatus>> &result)
181 {
182     for (const auto &work : works) {
183         auto iter = std::find_if(result.begin(), result.end(),
184         [&](const auto &existingWork) {
185             return existingWork->workId_ == work->workId_;
186         });
187         if (iter != result.end()) {
188             WS_HILOGE("WorkId:%{public}s existing, bundleName:%{public}s",
189                 work->workId_.c_str(), work->bundleName_.c_str());
190             continue;
191         }
192         result.push_back(work);
193     }
194 }
195 
OnConditionChanged(WorkCondition::Type conditionType,shared_ptr<DetectorValue> conditionVal)196 void WorkQueueManager::OnConditionChanged(WorkCondition::Type conditionType,
197     shared_ptr<DetectorValue> conditionVal)
198 {
199     auto service = wss_.lock();
200     if (!service) {
201         WS_HILOGE("service is null");
202         return;
203     }
204     auto task = [weak = weak_from_this(), service, conditionType, conditionVal]() {
205         auto strong = weak.lock();
206         if (!strong) {
207             WS_HILOGE("strong is null");
208             return;
209         }
210         vector<shared_ptr<WorkStatus>> readyWorkVector = strong->GetReayQueue(conditionType, conditionVal);
211         if (readyWorkVector.size() == 0) {
212             return;
213         }
214         for (auto it : readyWorkVector) {
215             it->MarkStatus(WorkStatus::Status::CONDITION_READY);
216         }
217         service->OnConditionReady(make_shared<vector<shared_ptr<WorkStatus>>>(readyWorkVector));
218     };
219     auto handler = service->GetHandler();
220     if (!handler) {
221         WS_HILOGE("handler is null");
222         return;
223     }
224     handler->PostTask(task);
225 }
226 
StopAndClearWorks(list<shared_ptr<WorkStatus>> workList)227 bool WorkQueueManager::StopAndClearWorks(list<shared_ptr<WorkStatus>> workList)
228 {
229     for (auto &it : workList) {
230         CancelWork(it);
231     }
232     return true;
233 }
234 
Dump(string & result)235 void WorkQueueManager::Dump(string& result)
236 {
237     std::lock_guard<ffrt::mutex> lock(mutex_);
238     string conditionType[] = {"network", "charger", "battery_status", "battery_level",
239         "storage", "timer", "group", "deepIdle", "standby", "unknown"};
240     uint32_t size = sizeof(conditionType);
241     for (auto it : queueMap_) {
242         if (it.first < size) {
243             result.append(conditionType[it.first]);
244         } else {
245             result.append(conditionType[size - 1]);
246         }
247         result.append(" : ");
248         result.append("[");
249         string workIdStr;
250         it.second->GetWorkIdStr(workIdStr);
251         result.append(workIdStr);
252         result.append("]\n");
253     }
254 }
255 
SetTimeCycle(uint32_t time)256 void WorkQueueManager::SetTimeCycle(uint32_t time)
257 {
258     timeCycle_ = time;
259     listenerMap_.at(WorkCondition::Type::TIMER)->Stop();
260     listenerMap_.at(WorkCondition::Type::TIMER)->Start();
261 }
262 
GetTimeCycle()263 uint32_t WorkQueueManager::GetTimeCycle()
264 {
265     return timeCycle_;
266 }
267 
SetTimeRetrigger(int32_t time)268 void WorkQueueManager::SetTimeRetrigger(int32_t time)
269 {
270     g_timeRetrigger = time;
271 }
272 
GetTimeRetrigger()273 int32_t WorkQueueManager::GetTimeRetrigger()
274 {
275     return g_timeRetrigger;
276 }
277 
SetMinIntervalByDump(int64_t interval)278 void WorkQueueManager::SetMinIntervalByDump(int64_t interval)
279 {
280     for (auto it : queueMap_) {
281         it.second->SetMinIntervalByDump(interval);
282     }
283 }
284 } // namespace WorkScheduler
285 } // namespace OHOS