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 <cstdint>
17 
18 #include <chrono>
19 #include <memory>
20 #include <mutex>
21 #include <optional>
22 #include <string>
23 #include <utility>
24 
25 #include <unistd.h>
26 
27 #include "plugin_mgr.h"
28 #include "rme_log_domain.h"
29 #include "event_runner.h"
30 #include "ffrt_inner.h"
31 
32 #include "latency_control/inetwork_latency_switcher.h"
33 #include "latency_control/network_latency_controller.h"
34 #include "latency_control/noop_network_latency_switcher.h"
35 #include "latency_control/pmqos_network_latency_switcher.h"
36 
37 #undef LOG_TAG
38 #define LOG_TAG "ueaServer-NetworkLatencyController"
39 
40 namespace OHOS::ResourceSchedule {
41 namespace {
42     const std::string NET_LATENCY_TIMER_NAME = "netLatTimer";
43     const std::chrono::duration TIMEOUT = std::chrono::seconds(60); // 1 minute timeout
44 }
45 
Init()46 void NetworkLatencyController::Init()
47 {
48     // use PMQoS switch if available
49     int err = access(PmqosNetworkLatencySwitcher::PMQOS_PATH.data(), W_OK);
50     if (!err) {
51         RME_LOGI("%{public}s: using pmqos latency switcher", __func__);
52         Init(std::make_unique<PmqosNetworkLatencySwitcher>());
53         return;
54     }
55 
56     // Another latency switchers can be implemented if required.
57     // If nothing matched, use default object, which is noop switcher.
58     RME_LOGI("%{public}s: using default latency switcher", __func__);
59     Init(std::make_unique<NoopNetworkLatencySwitcher>());
60 }
61 
Init(std::unique_ptr<INetworkLatencySwitcher> sw)62 void NetworkLatencyController::Init(std::unique_ptr<INetworkLatencySwitcher> sw)
63 {
64     {
65         std::unique_lock<ffrt::mutex> autoLock(latencyFfrtMutex_);
66         ffrtQueue_ = std::make_shared<ffrt::queue>("network_manager_ffrtQueue",
67             ffrt::queue_attr().qos(ffrt::qos_user_interactive));
68         if (ffrtQueue_ == nullptr) {
69             RME_LOGE("%{public}s: failed: cannot allocate event handler", __func__);
70             return;
71         }
72     }
73     std::unique_lock<std::mutex> lk(mtx);
74     switcher = std::move(sw);
75 }
76 
HandleRequest(long long value,const std::string & identity)77 void NetworkLatencyController::HandleRequest(long long value, const std::string &identity)
78 {
79     if (!switcher || ffrtQueue_ == nullptr) {
80         RME_LOGE("%{public}s: controller is not initialized", __func__);
81         return;
82     }
83 
84     switch (value) {
85         case NETWORK_LATENCY_REQUEST_LOW:
86             HandleAddRequest(identity);
87             break;
88         case NETWORK_LATENCY_REQUEST_NORMAL:
89             HandleDelRequest(identity);
90             break;
91         default:
92             RME_LOGW("%{public}s: invalid value: %{public}lld", __func__, value);
93             return;
94     }
95 }
96 
HandleAddRequest(const std::string & identity)97 void NetworkLatencyController::HandleAddRequest(const std::string &identity)
98 {
99     // cancel auto disable task first
100     {
101         std::unique_lock<ffrt::mutex> autoLock(latencyFfrtMutex_);
102         for (auto iter = taskHandlerMap_.begin(); iter != taskHandlerMap_.end();) {
103             if (iter->first == identity) {
104                 ffrtQueue_->cancel(iter->second);
105                 taskHandlerMap_.erase(iter++);
106             } else {
107                 iter++;
108             }
109         }
110     }
111 
112     RME_LOGD("%{public}s: add new request from %{public}s", __func__, identity.c_str());
113     AddRequest(identity);
114 
115     // set up the auto disable timer
116     {
117         std::unique_lock<ffrt::mutex> autoLock(latencyFfrtMutex_);
118         taskHandlerMap_[identity] = ffrtQueue_->submit_h(
119             [this, identity] { AutoDisableTask(identity); },
120             ffrt::task_attr().delay(
121                 std::chrono::duration_cast<std::chrono::milliseconds>(TIMEOUT).count() * switchToFfrt_
122             )
123         );
124     }
125 }
126 
HandleDelRequest(const std::string & identity)127 void NetworkLatencyController::HandleDelRequest(const std::string &identity)
128 {
129     // cancel auto disable task first
130     {
131         std::unique_lock<ffrt::mutex> autoLock(latencyFfrtMutex_);
132         for (auto iter = taskHandlerMap_.begin(); iter != taskHandlerMap_.end();) {
133             if (iter->first == identity) {
134                 ffrtQueue_->cancel(iter->second);
135                 taskHandlerMap_.erase(iter++);
136             } else {
137                 iter++;
138             }
139         }
140     }
141     RME_LOGD("%{public}s: delete request from %{public}s", __func__, identity.c_str());
142     DelRequest(identity);
143 }
144 
AddRequest(const std::string & identity)145 void NetworkLatencyController::AddRequest(const std::string &identity)
146 {
147     std::unique_lock<std::mutex> lk(mtx);
148     bool wasEmpty = requests.empty();
149     requests.insert(identity);
150 
151     // check whether it is the first request
152     if (wasEmpty) {
153         RME_LOGD("%{public}s: activating low latency", __func__);
154         switcher->LowLatencyOn();
155     }
156 }
157 
DelRequest(const std::string & identity)158 void NetworkLatencyController::DelRequest(const std::string &identity)
159 {
160     std::unique_lock<std::mutex> lk(mtx);
161     bool wasEmpty = requests.empty();
162     requests.erase(identity);
163 
164     // check whether is was the last request
165     if (!wasEmpty && requests.empty()) {
166         RME_LOGD("%{public}s: no callers left, restore normal latency", __func__);
167         switcher->LowLatencyOff();
168     }
169 }
170 
AutoDisableTask(const std::string & identity)171 void NetworkLatencyController::AutoDisableTask(const std::string &identity)
172 {
173     RME_LOGD("%{public}s: identity %{public}s timed out", __func__, identity.c_str());
174     DelRequest(identity);
175 }
176 } // namespace OHOS::ResourceSchedule
177