1 /*
2 * Copyright (c) 2021-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 "key_command_manager.h"
17 #include "ability_manager_client.h"
18 #include "file_ex.h"
19 #include "mmi_log.h"
20 #include "ohos/aafwk/base/string_wrapper.h"
21 #include "timer_manager.h"
22
23 namespace OHOS {
24 namespace MMI {
25 namespace {
26 constexpr int32_t MAX_PREKEYS_NUM = 4;
27 constexpr int32_t INVALID_VALUE = -1;
28 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, MMI_LOG_DOMAIN, "KeyCommandManager" };
29 }
30
KeyCommandManager()31 KeyCommandManager::KeyCommandManager()
32 {
33 const std::string configFile = GetConfigFilePath();
34 ResolveConfig(configFile);
35 Print();
36 }
37
GenerateKey(const ShortcutKey & key)38 std::string KeyCommandManager::GenerateKey(const ShortcutKey& key)
39 {
40 std::set<int32_t> preKeys = key.preKeys;
41 std::stringstream oss;
42 for (const auto preKey: preKeys) {
43 oss << preKey << ",";
44 }
45 oss << key.finalKey << ",";
46 oss << key.triggerType;
47 return std::string(oss.str());
48 }
49
GetConfigFilePath()50 std::string KeyCommandManager::GetConfigFilePath()
51 {
52 std::string defaultConfig = "/product/multimodalinput/ability_launch_config.json";
53 return FileExists(defaultConfig) ? defaultConfig : "/system/etc/multimodalinput/ability_launch_config.json";
54 }
55
ResolveConfig(const std::string configFile)56 void KeyCommandManager::ResolveConfig(const std::string configFile)
57 {
58 if (!FileExists(configFile)) {
59 MMI_LOGE("config file %{public}s not exist", configFile.c_str());
60 return;
61 }
62 MMI_LOGD("config file path:%{public}s", configFile.c_str());
63 std::ifstream reader(configFile);
64 if (!reader.is_open()) {
65 MMI_LOGE("config file open failed");
66 return;
67 }
68 json configJson;
69 reader >> configJson;
70 reader.close();
71 if (configJson.empty()) {
72 MMI_LOGE("config file is empty");
73 return;
74 }
75 json shortkeys = configJson["Shortkeys"];
76 if (!shortkeys.is_array() || shortkeys.empty()) {
77 MMI_LOGE("shortkeys in config file is empty");
78 return;
79 }
80 for (size_t i = 0; i < shortkeys.size(); i++) {
81 ShortcutKey shortcutKey;
82 if (!ConvertToShortcutKey(shortkeys[i], shortcutKey)) {
83 continue;
84 }
85 std::string key = GenerateKey(shortcutKey);
86 auto res = shortcutKeys_.find(key);
87 if (res == shortcutKeys_.end()) {
88 shortcutKeys_.emplace(key, shortcutKey);
89 }
90 }
91 }
92
ConvertToShortcutKey(const json & jsonData,ShortcutKey & shortcutKey)93 bool KeyCommandManager::ConvertToShortcutKey(const json &jsonData, ShortcutKey &shortcutKey)
94 {
95 json preKey = jsonData["preKey"];
96 if (!preKey.is_array() || preKey.size() > MAX_PREKEYS_NUM) {
97 MMI_LOGE("preKey number must less and equal four");
98 return false;
99 }
100
101 for (size_t i = 0; i < preKey.size(); i++) {
102 if (!preKey[i].is_number() || preKey[i] < 0) {
103 MMI_LOGE("preKey must be number and bigger and equal 0");
104 return false;
105 }
106 auto ret = shortcutKey.preKeys.emplace(preKey[i]);
107 if (!ret.second) {
108 MMI_LOGE("preKey must be unique");
109 return false;
110 }
111 }
112 if (!jsonData["finalKey"].is_number()) {
113 MMI_LOGE("finalKey must be number");
114 return false;
115 }
116 shortcutKey.finalKey = jsonData["finalKey"];
117 if ((!jsonData["trigger"].is_string()) ||
118 (jsonData["trigger"].get<std::string>() != "key_up" && jsonData["trigger"].get<std::string>() != "key_down")) {
119 MMI_LOGE("trigger must be one of [key_up, key_down]");
120 return false;
121 }
122 if (jsonData["trigger"].get<std::string>() == "key_up") {
123 shortcutKey.triggerType = KeyEvent::KEY_ACTION_UP;
124 } else {
125 shortcutKey.triggerType = KeyEvent::KEY_ACTION_DOWN;
126 }
127 if ((!jsonData["keyDownDuration"].is_number()) || (jsonData["keyDownDuration"] < 0)) {
128 MMI_LOGE("keyDownDuration must be number and bigger and equal zero");
129 return false;
130 }
131 shortcutKey.keyDownDuration = jsonData["keyDownDuration"];
132 if (!PackageAbility(jsonData["ability"], shortcutKey.ability)) {
133 MMI_LOGE("package ability failed!");
134 return false;
135 }
136 return true;
137 }
138
PackageAbility(const json & jsonAbility,Ability & ability)139 bool KeyCommandManager::PackageAbility(const json &jsonAbility, Ability &ability)
140 {
141 if (!jsonAbility.is_object()) {
142 MMI_LOGE("ability must be object");
143 return false;
144 }
145 if (!jsonAbility["entities"].is_array()) {
146 MMI_LOGE("entities must be array!");
147 return false;
148 }
149 if (!jsonAbility["params"].is_array()) {
150 MMI_LOGE("params must be array!");
151 return false;
152 }
153 ability.bundleName = jsonAbility["bundleName"];
154 ability.abilityName = jsonAbility["abilityName"];
155 ability.action = jsonAbility["action"];
156 ability.type = jsonAbility["type"];
157 ability.deviceId = jsonAbility["deviceId"];
158 ability.uri = jsonAbility["uri"];
159 for (size_t i = 0; i < jsonAbility["entities"].size(); i++) {
160 ability.entities.push_back(jsonAbility["entities"][i]);
161 }
162 json params = jsonAbility["params"];
163 for (size_t i = 0; i < params.size(); i++) {
164 if (!params[i].is_object()) {
165 MMI_LOGE("param must be object");
166 return false;
167 }
168 ability.params.emplace(params[i]["key"], params[i]["value"]);
169 }
170 return true;
171 }
172
Print()173 void KeyCommandManager::Print()
174 {
175 uint32_t count = shortcutKeys_.size();
176 MMI_LOGD("shortcutKey count:%{public}u", count);
177 for (const auto &item : shortcutKeys_) {
178 auto &shortcutKey = item.second;
179 for (auto prekey: shortcutKey.preKeys) {
180 MMI_LOGD("preKey:%{public}d", prekey);
181 }
182 MMI_LOGD("finalKey:%{public}d,keyDownDuration:%{public}d,triggerType:%{public}d,"
183 " bundleName:%{public}s,abilityName:%{public}s", shortcutKey.finalKey,
184 shortcutKey.keyDownDuration, shortcutKey.triggerType,
185 shortcutKey.ability.bundleName.c_str(), shortcutKey.ability.abilityName.c_str());
186 }
187 }
188
HandlerEvent(const std::shared_ptr<KeyEvent> event)189 bool KeyCommandManager::HandlerEvent(const std::shared_ptr<KeyEvent> event)
190 {
191 MMI_LOGD("enter");
192 if (IsKeyMatch(lastMatchedKey_, event)) {
193 MMI_LOGE("The same event is waiting timeout, skip");
194 return true;
195 }
196 if (lastMatchedKey_.timerId >= 0) {
197 MMI_LOGE("remove timer timeid:%{public}d", lastMatchedKey_.timerId);
198 TimerMgr->RemoveTimer(lastMatchedKey_.timerId);
199 }
200 ResetLastMatchedKey();
201 for (auto iter = shortcutKeys_.begin(); iter != shortcutKeys_.end(); ++iter) {
202 ShortcutKey &shortcutKey = iter->second;
203 if (!IsKeyMatch(shortcutKey, event)) {
204 MMI_LOGD("not matched, next");
205 continue;
206 }
207 for (const auto &prekey: shortcutKey.preKeys) {
208 MMI_LOGD("eventkey matched, preKey:%{public}d", prekey);
209 }
210 MMI_LOGD("eventkey matched, finalKey:%{public}d,bundleName:%{public}s",
211 shortcutKey.finalKey, shortcutKey.ability.bundleName.c_str());
212 if (shortcutKey.triggerType == KeyEvent::KEY_ACTION_DOWN) {
213 return HandleKeyDown(shortcutKey);
214 } else if (shortcutKey.triggerType == KeyEvent::KEY_ACTION_UP) {
215 return HandleKeyUp(event, shortcutKey);
216 } else {
217 return HandleKeyCancel(shortcutKey);
218 }
219 }
220 MMI_LOGD("leave");
221 return false;
222 }
223
IsKeyMatch(const ShortcutKey & shortcutKey,const std::shared_ptr<KeyEvent> & key)224 bool KeyCommandManager::IsKeyMatch(const ShortcutKey &shortcutKey, const std::shared_ptr<KeyEvent> &key)
225 {
226 MMI_LOGD("enter");
227 if (key->GetKeyCode() != shortcutKey.finalKey || shortcutKey.triggerType != key->GetKeyAction()) {
228 return false;
229 }
230 if ((shortcutKey.preKeys.size()) != (key->GetKeyItems().size() - 1)) { // KeyItems contain finalkey, so decrease 1
231 return false;
232 }
233 for (const auto &item : key->GetKeyItems()) {
234 int32_t keyCode = item.GetKeyCode();
235 if (keyCode == key->GetKeyCode()) {
236 continue;
237 }
238 auto res = shortcutKey.preKeys.find(keyCode);
239 if (res == shortcutKey.preKeys.end()) {
240 return false;
241 }
242 }
243 MMI_LOGD("matched...");
244 return true;
245 }
246
HandleKeyDown(ShortcutKey & shortcutKey)247 bool KeyCommandManager::HandleKeyDown(ShortcutKey &shortcutKey)
248 {
249 MMI_LOGD("enter");
250 if (shortcutKey.keyDownDuration == 0) {
251 MMI_LOGD("Start launch ability immediately");
252 LaunchAbility(shortcutKey);
253 } else {
254 shortcutKey.timerId = TimerMgr->AddTimer(shortcutKey.keyDownDuration, 1, [this, shortcutKey] () {
255 MMI_LOGD("Timer callback");
256 LaunchAbility(shortcutKey);
257 });
258 if (shortcutKey.timerId < 0) {
259 MMI_LOGE("Timer add failed");
260 return false;
261 }
262 MMI_LOGD("add timer success, timeid:%{public}d", shortcutKey.timerId);
263 lastMatchedKey_ = shortcutKey;
264 }
265 return true;
266 }
267
HandleKeyUp(const std::shared_ptr<KeyEvent> & keyEvent,const ShortcutKey & shortcutKey)268 bool KeyCommandManager::HandleKeyUp(const std::shared_ptr<KeyEvent> &keyEvent, const ShortcutKey &shortcutKey)
269 {
270 MMI_LOGD("enter");
271 if (shortcutKey.keyDownDuration == 0) {
272 MMI_LOGD("Start launch ability immediately");
273 LaunchAbility(shortcutKey);
274 return true;
275 } else {
276 const KeyEvent::KeyItem* keyItem = keyEvent->GetKeyItem();
277 CHKPF(keyItem);
278 auto upTime = keyEvent->GetActionTime();
279 auto downTime = keyItem->GetDownTime();
280 MMI_LOGD("UpTime:%{public}" PRId64 ",downTime:%{public}" PRId64 ",keyDownDuration:%{public}d",
281 upTime, downTime, shortcutKey.keyDownDuration);
282 if (upTime - downTime >= static_cast<int64_t>(shortcutKey.keyDownDuration) * 1000) {
283 MMI_LOGD("Skip, upTime - downTime >= duration");
284 return false;
285 }
286 MMI_LOGD("Start launch ability immediately");
287 LaunchAbility(shortcutKey);
288 return true;
289 }
290 }
291
HandleKeyCancel(ShortcutKey & shortcutKey)292 bool KeyCommandManager::HandleKeyCancel(ShortcutKey &shortcutKey)
293 {
294 MMI_LOGD("enter");
295 if (shortcutKey.timerId < 0) {
296 MMI_LOGE("Skip, timerid < 0");
297 }
298 auto timerId = shortcutKey.timerId;
299 shortcutKey.timerId = -1;
300 TimerMgr->RemoveTimer(timerId);
301 MMI_LOGD("Leave, timerId: %{public}d", timerId);
302 return false;
303 }
304
LaunchAbility(ShortcutKey key)305 void KeyCommandManager::LaunchAbility(ShortcutKey key)
306 {
307 AAFwk::Want want;
308 want.SetElementName(key.ability.deviceId, key.ability.bundleName, key.ability.abilityName);
309 want.SetAction(key.ability.action);
310 want.SetUri(key.ability.uri);
311 want.SetType(key.ability.uri);
312 for (const auto &entity : key.ability.entities) {
313 want.AddEntity(entity);
314 }
315 AAFwk::WantParams wParams;
316 for (const auto &item : key.ability.params) {
317 wParams.SetParam(item.first, AAFwk::String::Box(item.second));
318 }
319 want.SetParams(wParams);
320 MMI_LOGD("Start launch ability, bundleName:%{public}s", key.ability.bundleName.c_str());
321 ErrCode err = AAFwk::AbilityManagerClient::GetInstance()->StartAbility(want);
322 if (err != ERR_OK) {
323 MMI_LOGE("LaunchAbility failed, bundleName:%{public}s,err:%{public}d", key.ability.bundleName.c_str(), err);
324 }
325 ResetLastMatchedKey();
326 MMI_LOGD("End launch ability, bundleName:%{public}s", key.ability.bundleName.c_str());
327 }
328
ResetLastMatchedKey()329 void KeyCommandManager::ResetLastMatchedKey()
330 {
331 lastMatchedKey_.preKeys.clear();
332 lastMatchedKey_.finalKey = INVALID_VALUE;
333 lastMatchedKey_.timerId = INVALID_VALUE;
334 }
335
GetInstance()336 std::shared_ptr<IKeyCommandManager> IKeyCommandManager::GetInstance()
337 {
338 if (keyCommand_ == nullptr) {
339 keyCommand_ = std::make_shared<KeyCommandManager>();
340 }
341 return keyCommand_;
342 }
343 } // namespace MMI
344 } // namespace OHOS