1 /*
2  * Copyright (c) 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 <file_ex.h>
17 #include <set>
18 #include <sstream>
19 
20 #include "background_sensitive_task_overlapping_scene_recognizer.h"
21 #include "background_mode.h"
22 #include "res_sched_log.h"
23 #include "res_sched_mgr.h"
24 #include "res_type.h"
25 #include "string_ex.h"
26 
27 namespace OHOS {
28 namespace ResourceSchedule {
29 namespace {
30     static const int32_t INVALID_VALUE = -1;
31     static const std::string PID_KEY = "pid";
32     static const std::string TYPE_IDS_KEY = "typeIds";
33     static const std::set<uint32_t> PERCEIVABLE_MODES = {
34         BackgroundTaskMgr::BackgroundMode::Type::LOCATION,
35         BackgroundTaskMgr::BackgroundMode::Type::VOIP,
36         BackgroundTaskMgr::BackgroundMode::Type::AUDIO_PLAYBACK,
37         BackgroundTaskMgr::BackgroundMode::Type::MULTI_DEVICE_CONNECTION,
38     };
39 }
40 
~BackgroundSensitiveTaskOverlappingSceneRecognizer()41 BackgroundSensitiveTaskOverlappingSceneRecognizer::~BackgroundSensitiveTaskOverlappingSceneRecognizer()
42 {
43     RESSCHED_LOGI("~BackgroundPerceivableSceneRecoginzer");
44 }
45 
BackgroundSensitiveTaskOverlappingSceneRecognizer()46 BackgroundSensitiveTaskOverlappingSceneRecognizer::BackgroundSensitiveTaskOverlappingSceneRecognizer()
47 {
48     perceivableTasks_ = {};
49 }
50 
OnDispatchResource(uint32_t resType,int64_t value,const nlohmann::json & payload)51 void BackgroundSensitiveTaskOverlappingSceneRecognizer::OnDispatchResource(uint32_t resType, int64_t value,
52     const nlohmann::json& payload)
53 {
54     if (!payload.contains(PID_KEY) || !payload[PID_KEY].is_string()) {
55         return;
56     }
57     int32_t invalidValue = INVALID_VALUE;
58     switch (resType) {
59         case ResType::RES_TYPE_APP_STATE_CHANGE:
60             HandleForeground(resType, value, payload);
61             break;
62         case ResType::RES_TYPE_CONTINUOUS_TASK:
63             HandleContinuousTask(resType, value, payload);
64             break;
65         case ResType::RES_TYPE_REPORT_SCENE_BOARD:
66             StrToInt(payload[PID_KEY].get<std::string>(), sceneboardPid_);
67             break;
68         default:
69             break;
70     }
71 }
72 
HandleContinuousTask(uint32_t resType,int64_t value,const nlohmann::json & payload)73 void BackgroundSensitiveTaskOverlappingSceneRecognizer::HandleContinuousTask(uint32_t resType, int64_t value,
74     const nlohmann::json& payload)
75 {
76     pid_t pid = -1;
77     StrToInt(payload[PID_KEY].get<std::string>(), pid);
78     std::vector<uint32_t> typeIds;
79     if (payload.contains(TYPE_IDS_KEY) && payload[TYPE_IDS_KEY].is_array()) {
80         typeIds = payload[TYPE_IDS_KEY].get<std::vector<uint32_t>>();
81     }
82     std::stringstream typeIdsStr;
83     for (auto it = typeIds.begin(); it != typeIds.end();) {
84         if (!typeIdsStr.str().empty()) {
85             typeIdsStr << ", ";
86         }
87         typeIdsStr << *it;
88         if (PERCEIVABLE_MODES.find(*it) == PERCEIVABLE_MODES.end()) {
89             it = typeIds.erase(it);
90         } else {
91             it ++;
92         }
93     }
94     RESSCHED_LOGI("%{public}s, resType:%{public}d, value:%{public}lld, typeIds:{%{public}s}",
95         __func__, resType, (long long)value, typeIdsStr.str().c_str());
96     if (value == ResType::ContinuousTaskStatus::CONTINUOUS_TASK_START && !typeIds.empty()) {
97         HandleTaskStart(pid, typeIds);
98     } else if (value == ResType::ContinuousTaskStatus::CONTINUOUS_TASK_UPDATE) {
99         HandleTaskUpdate(pid, typeIds);
100     } else if (value == ResType::ContinuousTaskStatus::CONTINUOUS_TASK_END) {
101         HandleTaskStop(pid);
102     } else {
103         RESSCHED_LOGW("%{public}s, unknow ContinuousTaskStatus value", __func__);
104     }
105 }
106 
107 /**
108  * @brief judge is enter scene.
109  *  Rules for entering the BackgroundSensitiveTaskOverlapping scene:
110  *  1.Only one background sensitive continuous task,
111  *    and the foreground app is NOT the app that start the continous task or scene board.
112  *  2.Two or more background sensitive continous tasks, and the foregound app is NOT scene board.
113  * @return True if enter scene, else false.
114  */
CheckEnterScene()115 bool BackgroundSensitiveTaskOverlappingSceneRecognizer::CheckEnterScene()
116 {
117     if (isInBackgroundPerceivableScene_ || foregroundPid_ == sceneboardPid_) {
118         RESSCHED_LOGD("already in background sensitive scene or foreground is sceneboard");
119         return false;
120     }
121 
122     // more than one app has benn applied for sensitive task, there must be a task in background
123     if (perceivableTasks_.size() > 1) {
124         return true;
125     }
126     if (perceivableTasks_.size() > 0 && perceivableTasks_.find(foregroundPid_) == perceivableTasks_.end()) {
127         return true;
128     }
129     return false;
130 }
131 
EnterScene()132 void BackgroundSensitiveTaskOverlappingSceneRecognizer::EnterScene()
133 {
134     nlohmann::json payload;
135     ResSchedMgr::GetInstance().ReportData(ResType::RES_TYPE_BACKGROUND_PERCEIVABLE_SCENE,
136         ResType::BackgroundPerceivableStatus::PERCEIVABLE_START, payload);
137     isInBackgroundPerceivableScene_ = true;
138 }
139 
ExitScene()140 void BackgroundSensitiveTaskOverlappingSceneRecognizer::ExitScene()
141 {
142     nlohmann::json payload;
143     ResSchedMgr::GetInstance().ReportData(ResType::RES_TYPE_BACKGROUND_PERCEIVABLE_SCENE,
144         ResType::BackgroundPerceivableStatus::PERCEIVABLE_STOP, payload);
145     isInBackgroundPerceivableScene_ = false;
146 }
147 
HandleTaskStart(pid_t pid,const std::vector<uint32_t> & filteredTypeIds)148 void BackgroundSensitiveTaskOverlappingSceneRecognizer::HandleTaskStart(pid_t pid,
149     const std::vector<uint32_t> &filteredTypeIds)
150 {
151     perceivableTasks_[pid] = filteredTypeIds;
152     if (CheckEnterScene()) {
153         RESSCHED_LOGI("perceivable task start enter scene");
154         EnterScene();
155     }
156 }
157 
HandleTaskUpdate(pid_t pid,const std::vector<uint32_t> & filteredTypeIds)158 void BackgroundSensitiveTaskOverlappingSceneRecognizer::HandleTaskUpdate(pid_t pid,
159     const std::vector<uint32_t> &filteredTypeIds)
160 {
161     if (filteredTypeIds.empty()) {
162         if (perceivableTasks_.find(pid) != perceivableTasks_.end()) {
163             perceivableTasks_.erase(pid);
164         }
165         if (!CheckEnterScene() && isInBackgroundPerceivableScene_) {
166             RESSCHED_LOGI("after task update all perceivable task stop exit scene");
167             ExitScene();
168         }
169     } else {
170         perceivableTasks_[pid] = filteredTypeIds;
171         if (CheckEnterScene()) {
172             nlohmann::json payload;
173             RESSCHED_LOGI("after task update perceivable task update enter scene");
174             EnterScene();
175         }
176     }
177 }
178 
HandleTaskStop(pid_t pid)179 void BackgroundSensitiveTaskOverlappingSceneRecognizer::HandleTaskStop(pid_t pid)
180 {
181     if (perceivableTasks_.find(pid) != perceivableTasks_.end()) {
182         perceivableTasks_.erase(pid);
183         if (!CheckEnterScene() && isInBackgroundPerceivableScene_) {
184             RESSCHED_LOGI("after task stop all perceivable task stop exit scene");
185             ExitScene();
186         }
187     }
188 }
189 
HandleForeground(uint32_t resType,int64_t value,const nlohmann::json & payload)190 void BackgroundSensitiveTaskOverlappingSceneRecognizer::HandleForeground(uint32_t resType, int64_t value,
191     const nlohmann::json& payload)
192 {
193     if (value != ResType::ProcessStatus::PROCESS_FOREGROUND) {
194         return;
195     }
196     StrToInt(payload[PID_KEY].get<std::string>(), foregroundPid_);
197     if (foregroundPid_ == sceneboardPid_ && isInBackgroundPerceivableScene_) {
198         RESSCHED_LOGI("sceneboard foreground exit scene");
199         ExitScene();
200     } else if (CheckEnterScene()) {
201         RESSCHED_LOGI("sceneboard background and has perceivable task enter scene");
202         EnterScene();
203     }
204 }
205 } // namespace ResourceSchedule
206 } // namespace OHOS