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 "res_sched_log.h"
29 #include "event_handler.h"
30 #include "event_runner.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 namespace OHOS::ResourceSchedule {
38 namespace {
39 const std::string NET_LATENCY_TIMER_NAME = "netLatTimer";
40 const std::chrono::duration TIMEOUT = std::chrono::seconds(60); // 1 minute timeout
41 }
42
Init()43 void NetworkLatencyController::Init()
44 {
45 // use PMQoS switch if available
46 int err = access(PmqosNetworkLatencySwitcher::PMQOS_PATH.data(), W_OK);
47 if (!err) {
48 RESSCHED_LOGI("%{public}s: using pmqos latency switcher", __func__);
49 Init(std::make_unique<PmqosNetworkLatencySwitcher>());
50 return;
51 }
52
53 // Another latency switchers can be implemented if required.
54 // If nothing matched, use default object, which is noop switcher.
55 RESSCHED_LOGI("%{public}s: using default latency switcher", __func__);
56 Init(std::make_unique<NoopNetworkLatencySwitcher>());
57 }
58
Init(std::unique_ptr<INetworkLatencySwitcher> sw)59 void NetworkLatencyController::Init(std::unique_ptr<INetworkLatencySwitcher> sw)
60 {
61 handler = std::make_shared<AppExecFwk::EventHandler>(
62 PluginMgr::GetInstance().GetRunner()
63 );
64 if (!handler) {
65 RESSCHED_LOGE("%{public}s: failed: cannot allocate event handler", __func__);
66 return;
67 }
68
69 switcher = std::move(sw);
70 }
71
HandleRequest(long long value,const std::string & identity)72 void NetworkLatencyController::HandleRequest(long long value, const std::string &identity)
73 {
74 if (!switcher || !handler) {
75 RESSCHED_LOGE("%{public}s: controller is not initialized", __func__);
76 return;
77 }
78
79 switch (value) {
80 case NETWORK_LATENCY_REQUEST_LOW:
81 HandleAddRequest(identity);
82 break;
83 case NETWORK_LATENCY_REQUEST_NORMAL:
84 HandleDelRequest(identity);
85 break;
86 default:
87 RESSCHED_LOGW("%{public}s: invalid value: %{public}lld", __func__, value);
88 return;
89 }
90 }
91
HandleAddRequest(const std::string & identity)92 void NetworkLatencyController::HandleAddRequest(const std::string &identity)
93 {
94 // cancel auto disable task first
95 handler->RemoveTask(identity);
96 std::unique_lock<std::mutex> lk(mtx);
97
98 RESSCHED_LOGD("%{public}s: add new request from %{public}s", __func__, identity.c_str());
99 AddRequest(identity);
100
101 // set up the auto disable timer
102 handler->PostTask(
103 [this, identity] { AutoDisableTask(identity); },
104 identity, // use the identity as a key to manage this task
105 std::chrono::duration_cast<std::chrono::milliseconds>(TIMEOUT).count()
106 );
107 }
108
HandleDelRequest(const std::string & identity)109 void NetworkLatencyController::HandleDelRequest(const std::string &identity)
110 {
111 // cancel auto disable task first
112 handler->RemoveTask(identity);
113 std::unique_lock<std::mutex> lk(mtx);
114
115 RESSCHED_LOGD("%{public}s: delete request from %{public}s", __func__, identity.c_str());
116 DelRequest(identity);
117 }
118
AddRequest(const std::string & identity)119 void NetworkLatencyController::AddRequest(const std::string &identity)
120 {
121 bool wasEmpty = requests.empty();
122 requests.insert(identity);
123
124 // check whether it is the first request
125 if (wasEmpty) {
126 RESSCHED_LOGD("%{public}s: activating low latency", __func__);
127 switcher->LowLatencyOn();
128 }
129 }
130
DelRequest(const std::string & identity)131 void NetworkLatencyController::DelRequest(const std::string &identity)
132 {
133 bool wasEmpty = requests.empty();
134 requests.erase(identity);
135
136 // check whether is was the last request
137 if (!wasEmpty && requests.empty()) {
138 RESSCHED_LOGD("%{public}s: no callers left, restore normal latency", __func__);
139 switcher->LowLatencyOff();
140 }
141 }
142
AutoDisableTask(const std::string & identity)143 void NetworkLatencyController::AutoDisableTask(const std::string &identity)
144 {
145 std::unique_lock<std::mutex> lk(mtx);
146
147 RESSCHED_LOGD("%{public}s: identity %{public}s timed out", __func__, identity.c_str());
148 DelRequest(identity);
149 }
150 } // namespace OHOS::ResourceSchedule
151