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 "key_shortcut_manager.h"
17
18 #include "app_state_observer.h"
19 #include "key_command_handler_util.h"
20
21 #undef MMI_LOG_DOMAIN
22 #define MMI_LOG_DOMAIN MMI_LOG_HANDLER
23 #undef MMI_LOG_TAG
24 #define MMI_LOG_TAG "KeyShortcutManager"
25
26 namespace OHOS {
27 namespace MMI {
28 namespace {
29 constexpr size_t SINGLE_MODIFIER { 1 };
30 constexpr size_t MAX_N_PRINTABLE_ITEMS { 3 };
31 constexpr int32_t MAXIMUM_LONG_PRESS_TIME { 60000 }; // 60s
32 constexpr int32_t REPEAT_ONCE { 1 };
33 }
34
35 std::mutex KeyShortcutManager::mutex_;
36 std::shared_ptr<KeyShortcutManager> KeyShortcutManager::instance_;
37
38 const std::map<int32_t, uint32_t> KeyShortcutManager::modifiers_ {
39 { KeyEvent::KEYCODE_ALT_LEFT, SHORTCUT_MODIFIER_ALT },
40 { KeyEvent::KEYCODE_ALT_RIGHT, SHORTCUT_MODIFIER_ALT },
41 { KeyEvent::KEYCODE_SHIFT_LEFT, SHORTCUT_MODIFIER_SHIFT },
42 { KeyEvent::KEYCODE_SHIFT_RIGHT, SHORTCUT_MODIFIER_SHIFT },
43 { KeyEvent::KEYCODE_CTRL_LEFT, SHORTCUT_MODIFIER_CTRL },
44 { KeyEvent::KEYCODE_CTRL_RIGHT, SHORTCUT_MODIFIER_CTRL },
45 { KeyEvent::KEYCODE_META_LEFT, SHORTCUT_MODIFIER_LOGO },
46 { KeyEvent::KEYCODE_META_RIGHT, SHORTCUT_MODIFIER_LOGO }
47 };
48
operator <(const SystemKey & other) const49 bool KeyShortcutManager::SystemKey::operator<(const SystemKey &other) const
50 {
51 uint32_t modifier1 = (modifiers & SHORTCUT_MODIFIER_MASK);
52 uint32_t modifier2 = (other.modifiers & SHORTCUT_MODIFIER_MASK);
53 if (modifier1 != modifier2) {
54 return (modifier1 < modifier2);
55 }
56 return (finalKey < other.finalKey);
57 }
58
operator <(const ExceptionalSystemKey & other) const59 bool KeyShortcutManager::ExceptionalSystemKey::operator<(const ExceptionalSystemKey &other) const
60 {
61 if (finalKey != other.finalKey) {
62 return (finalKey < other.finalKey);
63 }
64 if (longPressTime != other.longPressTime) {
65 return (longPressTime < other.longPressTime);
66 }
67 if (triggerType != other.triggerType) {
68 return (triggerType < other.triggerType);
69 }
70 return (preKeys < other.preKeys);
71 }
72
operator <(const SystemHotkey & other) const73 bool KeyShortcutManager::SystemHotkey::operator<(const SystemHotkey &other) const
74 {
75 if (finalKey != other.finalKey) {
76 return (finalKey < other.finalKey);
77 }
78 return (preKeys < other.preKeys);
79 }
80
GetInstance()81 std::shared_ptr<KeyShortcutManager> KeyShortcutManager::GetInstance()
82 {
83 if (instance_ == nullptr) {
84 std::lock_guard<std::mutex> guard(mutex_);
85 if (instance_ == nullptr) {
86 instance_ = std::make_shared<KeyShortcutManager>();
87 }
88 }
89 return instance_;
90 }
91
KeyShortcutManager()92 KeyShortcutManager::KeyShortcutManager()
93 {
94 LoadSystemKeys();
95 LoadExceptionalSystemKeys();
96 LoadHotkeys();
97 }
98
RegisterSystemKey(const SystemShortcutKey & key)99 int32_t KeyShortcutManager::RegisterSystemKey(const SystemShortcutKey &key)
100 {
101 KeyShortcut shortcut {};
102 ExceptionalSystemKey eSysKey {
103 .preKeys = key.modifiers,
104 .finalKey = key.finalKey,
105 .longPressTime = key.longPressTime,
106 .triggerType = key.triggerType,
107 };
108
109 if (!CheckSystemKey(key, shortcut)) {
110 MMI_HILOGE("Not system key ([%{public}s],FinalKey:%{public}d,PressTime:%{public}d,TriggerType:%{public}d)",
111 FormatModifiers(key.modifiers).c_str(), key.finalKey, key.longPressTime, key.triggerType);
112 if (IsExceptionalSystemKey(eSysKey)) {
113 auto shortcutId = GenerateId();
114 MMI_HILOGI("Register exceptional system key [No.%{public}d]"
115 "([%{public}s],FinalKey:%{public}d,PressTime:%{public}d,TriggerType:%{public}d)",
116 shortcutId, FormatModifiers(key.modifiers).c_str(), key.finalKey, key.longPressTime, key.triggerType);
117 return shortcutId;
118 }
119 return KEY_SHORTCUT_ERROR_COMBINATION_KEY;
120 }
121 if (!IsReservedSystemKey(shortcut)) {
122 if (IsExceptionalSystemKey(eSysKey)) {
123 auto shortcutId = GenerateId();
124 MMI_HILOGI("Register exceptional system key [No.%{public}d]"
125 "([%{public}s],FinalKey:%{public}d,PressTime:%{public}d,TriggerType:%{public}d)",
126 shortcutId, FormatModifiers(key.modifiers).c_str(), key.finalKey, key.longPressTime, key.triggerType);
127 return shortcutId;
128 }
129 MMI_HILOGE("The system application can only subscribe to reserved shortcuts");
130 return KEY_SHORTCUT_ERROR_COMBINATION_KEY;
131 }
132 auto [iter, _] = shortcuts_.emplace(GenerateId(), shortcut);
133 MMI_HILOGI("Register system key [No.%{public}d](0x%{public}x,%{public}d,%{public}d,%{public}d,%{public}d)",
134 iter->first, shortcut.modifiers, shortcut.finalKey, shortcut.longPressTime,
135 shortcut.triggerType, shortcut.session);
136 return iter->first;
137 }
138
UnregisterSystemKey(int32_t shortcutId)139 void KeyShortcutManager::UnregisterSystemKey(int32_t shortcutId)
140 {
141 auto iter = shortcuts_.find(shortcutId);
142 if (iter == shortcuts_.end()) {
143 MMI_HILOGI("There is no system key(%{public}d)", shortcutId);
144 return;
145 }
146 const KeyShortcut &key = iter->second;
147 MMI_HILOGI("Unregister system key(0x%{public}x,%{public}d,%{public}d,%{public}d,SESSION:%{public}d)",
148 key.modifiers, key.finalKey, key.longPressTime, key.triggerType, key.session);
149 ResetTriggering(shortcutId);
150 shortcuts_.erase(iter);
151 }
152
RegisterHotKey(const HotKey & key)153 int32_t KeyShortcutManager::RegisterHotKey(const HotKey &key)
154 {
155 KeyShortcut globalKey {};
156
157 if (!CheckGlobalKey(key, globalKey)) {
158 MMI_HILOGE("Not global shortcut key");
159 return KEY_SHORTCUT_ERROR_COMBINATION_KEY;
160 }
161 if (HaveRegisteredGlobalKey(globalKey)) {
162 MMI_HILOGE("Global key (0x%{public}x, %{public}d) has been taken", globalKey.modifiers, globalKey.finalKey);
163 return KEY_SHORTCUT_ERROR_TAKEN;
164 }
165 if (IsReservedSystemKey(globalKey)) {
166 MMI_HILOGE("Can not register reserved system key ([%{public}s],%{public}d)",
167 FormatModifiers(key.modifiers).c_str(), key.finalKey);
168 return KEY_SHORTCUT_ERROR_COMBINATION_KEY;
169 }
170 auto [iter, _] = shortcuts_.emplace(GenerateId(), globalKey);
171 MMI_HILOGI("Register global key [No.%{public}d](0x%{public}x,%{public}d,SESSION:%{public}d)",
172 iter->first, globalKey.modifiers, globalKey.finalKey, globalKey.session);
173 return iter->first;
174 }
175
UnregisterHotKey(int32_t shortcutId)176 void KeyShortcutManager::UnregisterHotKey(int32_t shortcutId)
177 {
178 auto iter = shortcuts_.find(shortcutId);
179 if (iter == shortcuts_.end()) {
180 MMI_HILOGI("There is no global key(%{public}d)", shortcutId);
181 return;
182 }
183 const KeyShortcut &key = iter->second;
184 MMI_HILOGI("Unregister global key(0x%{public}x,%{public}d,SESSION:%{public}d)",
185 key.modifiers, key.finalKey, key.session);
186 shortcuts_.erase(iter);
187 }
188
HandleEvent(std::shared_ptr<KeyEvent> keyEvent)189 bool KeyShortcutManager::HandleEvent(std::shared_ptr<KeyEvent> keyEvent)
190 {
191 CHKPF(keyEvent);
192 MMI_HILOGI("Handle key event(No.%{public}d,KC:%{public}d,KA:%{public}d,PressedKeys:[%{public}s])",
193 keyEvent->GetId(), keyEvent->GetKeyCode(), keyEvent->GetKeyAction(), FormatPressedKeys(keyEvent).c_str());
194 ResetTriggering(keyEvent);
195 if (keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_DOWN) {
196 return HandleKeyDown(keyEvent);
197 } else if (keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_UP) {
198 return HandleKeyUp(keyEvent);
199 } else if (keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_CANCEL) {
200 return HandleKeyCancel(keyEvent);
201 }
202 return false;
203 }
204
LoadSystemKeys()205 void KeyShortcutManager::LoadSystemKeys()
206 {
207 char cfgName[] { "etc/multimodalinput/system_keys_config.json" };
208 char buf[MAX_PATH_LEN] {};
209 char *cfgPath = ::GetOneCfgFile(cfgName, buf, sizeof(buf));
210
211 if (cfgPath == nullptr) {
212 MMI_HILOGE("No '%{public}s' was found", cfgName);
213 return;
214 }
215 MMI_HILOGI("Config of system keys:%{public}s", cfgPath);
216 ReadSystemKeys(std::string(cfgPath));
217 }
218
ReadSystemKeys(const std::string & cfgPath)219 void KeyShortcutManager::ReadSystemKeys(const std::string &cfgPath)
220 {
221 std::string cfg = ReadJsonFile(cfgPath);
222 JsonParser parser;
223 parser.json_ = cJSON_Parse(cfg.c_str());
224 if (!cJSON_IsObject(parser.json_)) {
225 MMI_HILOGE("Not json format");
226 return;
227 }
228 cJSON* jsonSysKeys = cJSON_GetObjectItemCaseSensitive(parser.json_, "SystemKeys");
229 if (!cJSON_IsArray(jsonSysKeys)) {
230 MMI_HILOGE("The system keys is not array");
231 return;
232 }
233 int32_t nSysKeys = cJSON_GetArraySize(jsonSysKeys);
234 for (int32_t index = 0; index < nSysKeys; ++index) {
235 cJSON *jsonSysKey = cJSON_GetArrayItem(jsonSysKeys, index);
236 ReadSystemKey(jsonSysKey);
237 }
238 }
239
ReadSystemKey(cJSON * jsonSysKey)240 int32_t KeyShortcutManager::ReadSystemKey(cJSON *jsonSysKey)
241 {
242 if (!cJSON_IsObject(jsonSysKey)) {
243 MMI_HILOGE("Not json object");
244 return KEY_SHORTCUT_ERROR_CONFIG;
245 }
246 cJSON *jsonPreKeys = cJSON_GetObjectItem(jsonSysKey, "preKeys");
247 if (!cJSON_IsArray(jsonPreKeys)) {
248 MMI_HILOGE("Expect array for PreKeys");
249 return KEY_SHORTCUT_ERROR_CONFIG;
250 }
251 std::set<int32_t> preKeys;
252 int32_t nPreKeys = cJSON_GetArraySize(jsonPreKeys);
253
254 for (int32_t index = 0; index < nPreKeys; ++index) {
255 cJSON *jsonPreKey = cJSON_GetArrayItem(jsonPreKeys, index);
256 if (!cJSON_IsNumber(jsonPreKey)) {
257 MMI_HILOGE("Expect number for PreKey");
258 return KEY_SHORTCUT_ERROR_CONFIG;
259 }
260 preKeys.insert(static_cast<int32_t>(cJSON_GetNumberValue(jsonPreKey)));
261 }
262 cJSON *jsonFinalKey = cJSON_GetObjectItem(jsonSysKey, "finalKey");
263 if (!cJSON_IsNumber(jsonFinalKey)) {
264 MMI_HILOGE("Expect number for FinalKey");
265 return KEY_SHORTCUT_ERROR_CONFIG;
266 }
267 int32_t finalKey = static_cast<int32_t>(cJSON_GetNumberValue(jsonFinalKey));
268 return AddSystemKey(preKeys, finalKey);
269 }
270
AddSystemKey(const std::set<int32_t> & preKeys,int32_t finalKey)271 int32_t KeyShortcutManager::AddSystemKey(const std::set<int32_t> &preKeys, int32_t finalKey)
272 {
273 SystemShortcutKey sysKey {
274 .modifiers = preKeys,
275 .finalKey = finalKey,
276 };
277 KeyShortcut shortcut {};
278
279 if (!CheckSystemKey(sysKey, shortcut)) {
280 MMI_HILOGE("Not system key ([%{public}s],%{public}d)", FormatModifiers(preKeys).c_str(), finalKey);
281 return KEY_SHORTCUT_ERROR_COMBINATION_KEY;
282 }
283 systemKeys_.emplace(SystemKey {
284 .modifiers = shortcut.modifiers,
285 .finalKey = shortcut.finalKey,
286 });
287 return RET_OK;
288 }
289
LoadExceptionalSystemKeys()290 void KeyShortcutManager::LoadExceptionalSystemKeys()
291 {
292 char cfgName[] { "etc/multimodalinput/exceptional_system_keys_config.json" };
293 char buf[MAX_PATH_LEN] {};
294 char *cfgPath = ::GetOneCfgFile(cfgName, buf, sizeof(buf));
295
296 if (cfgPath == nullptr) {
297 MMI_HILOGE("GetoneCfgFile failed");
298 return;
299 }
300 MMI_HILOGI("Config of exceptional system keys:%{public}s", cfgPath);
301 ReadExceptionalSystemKeys(std::string(cfgPath));
302 }
303
ReadExceptionalSystemKeys(const std::string & cfgPath)304 void KeyShortcutManager::ReadExceptionalSystemKeys(const std::string &cfgPath)
305 {
306 std::string cfg = ReadJsonFile(cfgPath);
307 JsonParser parser;
308 parser.json_ = cJSON_Parse(cfg.c_str());
309 if (!cJSON_IsObject(parser.json_)) {
310 MMI_HILOGE("Not json format");
311 return;
312 }
313 cJSON* jsonSysKeys = cJSON_GetObjectItemCaseSensitive(parser.json_, "ExceptionalSystemKeys");
314 if (!cJSON_IsArray(jsonSysKeys)) {
315 MMI_HILOGE("The exceptional system keys is not array");
316 return;
317 }
318 int32_t nSysKeys = cJSON_GetArraySize(jsonSysKeys);
319 for (int32_t index = 0; index < nSysKeys; ++index) {
320 cJSON *jsonSysKey = cJSON_GetArrayItem(jsonSysKeys, index);
321 ReadExceptionalSystemKey(jsonSysKey);
322 }
323 }
324
ReadExceptionalSystemKey(cJSON * jsonSysKey)325 int32_t KeyShortcutManager::ReadExceptionalSystemKey(cJSON *jsonSysKey)
326 {
327 if (!cJSON_IsObject(jsonSysKey)) {
328 MMI_HILOGE("Not json object");
329 return KEY_SHORTCUT_ERROR_CONFIG;
330 }
331 ExceptionalSystemKey sysKey {};
332 cJSON *jsonPreKeys = cJSON_GetObjectItem(jsonSysKey, "preKeys");
333 if (!cJSON_IsArray(jsonPreKeys)) {
334 MMI_HILOGE("Expect array for PreKeys");
335 return KEY_SHORTCUT_ERROR_CONFIG;
336 }
337 int32_t nPreKeys = cJSON_GetArraySize(jsonPreKeys);
338
339 for (int32_t index = 0; index < nPreKeys; ++index) {
340 cJSON *jsonPreKey = cJSON_GetArrayItem(jsonPreKeys, index);
341 if (!cJSON_IsNumber(jsonPreKey)) {
342 MMI_HILOGE("Expect number for PreKey");
343 return KEY_SHORTCUT_ERROR_CONFIG;
344 }
345 sysKey.preKeys.insert(static_cast<int32_t>(cJSON_GetNumberValue(jsonPreKey)));
346 }
347 cJSON *jsonFinalKey = cJSON_GetObjectItem(jsonSysKey, "finalKey");
348 if (!cJSON_IsNumber(jsonFinalKey)) {
349 MMI_HILOGE("Expect number for FinalKey");
350 return KEY_SHORTCUT_ERROR_CONFIG;
351 }
352 sysKey.finalKey = static_cast<int32_t>(cJSON_GetNumberValue(jsonFinalKey));
353
354 cJSON *jsonPressTime = cJSON_GetObjectItem(jsonSysKey, "longPressTime");
355 if (!cJSON_IsNumber(jsonPressTime)) {
356 MMI_HILOGE("Expect number for LongPressTime");
357 return KEY_SHORTCUT_ERROR_CONFIG;
358 }
359 sysKey.longPressTime = static_cast<int32_t>(cJSON_GetNumberValue(jsonPressTime));
360
361 cJSON *jsonTriggerType = cJSON_GetObjectItem(jsonSysKey, "triggerType");
362 char *triggerType = cJSON_GetStringValue(jsonTriggerType);
363 if ((triggerType == nullptr) ||
364 ((std::strcmp(triggerType, "down") != 0) && (std::strcmp(triggerType, "up") != 0))) {
365 MMI_HILOGE("Expect down/up for TriggerType");
366 return KEY_SHORTCUT_ERROR_CONFIG;
367 }
368 sysKey.triggerType = (std::strcmp(triggerType, "down") == 0 ?
369 SHORTCUT_TRIGGER_TYPE_DOWN : SHORTCUT_TRIGGER_TYPE_UP);
370
371 AddExceptionalSystemKey(sysKey);
372 return RET_OK;
373 }
374
AddExceptionalSystemKey(const ExceptionalSystemKey & sysKey)375 void KeyShortcutManager::AddExceptionalSystemKey(const ExceptionalSystemKey &sysKey)
376 {
377 MMI_HILOGI("Add exceptional system key ([%{public}s],FinalKey:%{public}d,PressTime:%{public}d,%{public}s)",
378 FormatModifiers(sysKey.preKeys).c_str(), sysKey.finalKey, sysKey.longPressTime,
379 (sysKey.triggerType == SHORTCUT_TRIGGER_TYPE_DOWN ? "down" : "up"));
380 exceptSysKeys_.emplace(sysKey);
381 }
382
FormatModifiers(const std::set<int32_t> & modifiers) const383 std::string KeyShortcutManager::FormatModifiers(const std::set<int32_t> &modifiers) const
384 {
385 std::ostringstream sModifiers;
386 size_t nModifiers = 0;
387
388 if (auto iter = modifiers.cbegin(); iter != modifiers.cend()) {
389 sModifiers << *iter;
390 ++nModifiers;
391
392 for (++iter; iter != modifiers.cend(); ++iter) {
393 if (nModifiers > MAX_N_PRINTABLE_ITEMS) {
394 sModifiers << ",...";
395 break;
396 }
397 sModifiers << "," << *iter;
398 ++nModifiers;
399 }
400 }
401 return sModifiers.str();
402 }
403
GenerateId() const404 int32_t KeyShortcutManager::GenerateId() const
405 {
406 static int32_t baseId {};
407 return ++baseId;
408 }
409
IsExceptionalSystemKey(const ExceptionalSystemKey & sysKey) const410 bool KeyShortcutManager::IsExceptionalSystemKey(const ExceptionalSystemKey &sysKey) const
411 {
412 return (exceptSysKeys_.find(sysKey) != exceptSysKeys_.cend());
413 }
414
IsModifier(int32_t keyCode)415 bool KeyShortcutManager::IsModifier(int32_t keyCode)
416 {
417 return (modifiers_.find(keyCode) != modifiers_.cend());
418 }
419
IsValid(const ShortcutTriggerType triggerType) const420 bool KeyShortcutManager::IsValid(const ShortcutTriggerType triggerType) const
421 {
422 return ((triggerType == SHORTCUT_TRIGGER_TYPE_DOWN) ||
423 (triggerType == SHORTCUT_TRIGGER_TYPE_UP));
424 }
425
IsReservedSystemKey(const KeyShortcut & shortcut) const426 bool KeyShortcutManager::IsReservedSystemKey(const KeyShortcut &shortcut) const
427 {
428 return (systemKeys_.find(SystemKey {
429 .modifiers = shortcut.modifiers,
430 .finalKey = shortcut.finalKey,
431 }) != systemKeys_.cend());
432 }
433
CheckSystemKey(const SystemShortcutKey & key,KeyShortcut & shortcut) const434 bool KeyShortcutManager::CheckSystemKey(const SystemShortcutKey &key, KeyShortcut &shortcut) const
435 {
436 size_t nModifiers = 0;
437 uint32_t modifiers = 0U;
438
439 for (auto keyCode : key.modifiers) {
440 auto iter = modifiers_.find(keyCode);
441 if (iter == modifiers_.end()) {
442 MMI_HILOGE("Key code (%{public}d) is not modifier", keyCode);
443 return false;
444 }
445 if ((modifiers & iter->second) != iter->second) {
446 modifiers |= iter->second;
447 ++nModifiers;
448 }
449 }
450 if (nModifiers < SINGLE_MODIFIER) {
451 MMI_HILOGD("Require modifier(s)");
452 return false;
453 }
454 if (key.finalKey == SHORTCUT_PURE_MODIFIERS) {
455 if ((nModifiers == SINGLE_MODIFIER) && (modifiers != SHORTCUT_MODIFIER_LOGO)) {
456 MMI_HILOGE("Only 'Logo' can be one-key shortcut");
457 return false;
458 }
459 } else if (IsModifier(key.finalKey)) {
460 MMI_HILOGE("Modifier as final key");
461 return false;
462 }
463 if (!IsValid(key.triggerType)) {
464 MMI_HILOGE("Invalid trigger type(%{public}d)", key.triggerType);
465 return false;
466 }
467 if ((key.longPressTime < 0) || (key.longPressTime > MAXIMUM_LONG_PRESS_TIME)) {
468 MMI_HILOGE("Long-press time(%{public}d) is out of range [0,%{public}d]",
469 key.longPressTime, MAXIMUM_LONG_PRESS_TIME);
470 return false;
471 }
472 shortcut = KeyShortcut {
473 .modifiers = modifiers,
474 .finalKey = key.finalKey,
475 .longPressTime = key.longPressTime,
476 .triggerType = key.triggerType,
477 .session = key.session,
478 .callback = key.callback,
479 };
480 return true;
481 }
482
CheckGlobalKey(const HotKey & key,KeyShortcut & shortcut) const483 bool KeyShortcutManager::CheckGlobalKey(const HotKey &key, KeyShortcut &shortcut) const
484 {
485 size_t nModifiers = 0;
486 uint32_t modifiers = 0U;
487
488 for (auto keyCode : key.modifiers) {
489 auto iter = modifiers_.find(keyCode);
490 if (iter == modifiers_.end()) {
491 MMI_HILOGE("Key code (%{public}d) is not modifier", keyCode);
492 return false;
493 }
494 if ((modifiers & iter->second) != iter->second) {
495 modifiers |= iter->second;
496 ++nModifiers;
497 }
498 }
499 if (IsModifier(key.finalKey)) {
500 MMI_HILOGE("FinalKey(%{public}d) should not be modifier", key.finalKey);
501 return false;
502 }
503 if (key.finalKey == SHORTCUT_PURE_MODIFIERS) {
504 MMI_HILOGE("Expect FinalKey");
505 return false;
506 }
507 if (modifiers & SHORTCUT_MODIFIER_LOGO) {
508 MMI_HILOGE("'LOGO' is not allowed for GlobalKey");
509 return false;
510 }
511 if (nModifiers < SINGLE_MODIFIER) {
512 MMI_HILOGE("Require modifier(s)");
513 return false;
514 }
515 shortcut = KeyShortcut {
516 .modifiers = modifiers,
517 .finalKey = key.finalKey,
518 .triggerType = SHORTCUT_TRIGGER_TYPE_DOWN,
519 .session = key.session,
520 .callback = key.callback,
521 };
522 return true;
523 }
524
HaveRegisteredGlobalKey(const KeyShortcut & key) const525 bool KeyShortcutManager::HaveRegisteredGlobalKey(const KeyShortcut &key) const
526 {
527 auto iter = std::find_if(shortcuts_.cbegin(), shortcuts_.cend(),
528 [&key](const auto &item) {
529 return ((item.second.modifiers == key.modifiers) &&
530 (item.second.finalKey == key.finalKey));
531 });
532 // We met the problem: key-shortcut does not differentiate left/right CTRL/SHIFT/ALT/LOGO.
533 // but the implementation of key-shortcut reuse the logic of key-subscription, which
534 // treat left/right CTRL/SHIFT/ALT/LOGO as different keys. That means, for 'CTRL+A' etc
535 // to work as expected, we have to subscribe both 'LEFT-CTRL+A' and 'RIGHT-CTRL+A'.
536 // But duplicate global key registration will fail according to key-shortcut rules.
537 // We relax this retriction here to allow duplicate global key registration from same application.
538 return (iter != shortcuts_.cend() ? (iter->second.session != key.session) : false);
539 }
540
FormatPressedKeys(std::shared_ptr<KeyEvent> keyEvent) const541 std::string KeyShortcutManager::FormatPressedKeys(std::shared_ptr<KeyEvent> keyEvent) const
542 {
543 auto pressedKeys = keyEvent->GetPressedKeys();
544 std::ostringstream sPressedKeys;
545 size_t nPressedKeys = 0;
546
547 if (auto iter = pressedKeys.cbegin(); iter != pressedKeys.cend()) {
548 sPressedKeys << *iter;
549 ++nPressedKeys;
550
551 for (++iter; iter != pressedKeys.cend(); ++iter) {
552 if (nPressedKeys > MAX_N_PRINTABLE_ITEMS) {
553 sPressedKeys << ",...";
554 break;
555 }
556 sPressedKeys << "," << *iter;
557 ++nPressedKeys;
558 }
559 }
560 return sPressedKeys.str();
561 }
562
GetForegroundPids() const563 std::set<int32_t> KeyShortcutManager::GetForegroundPids() const
564 {
565 std::vector<AppExecFwk::AppStateData> foregroundApps = APP_OBSERVER_MGR->GetForegroundAppData();
566 std::set<int32_t> foregroundPids;
567
568 for (auto &item : foregroundApps) {
569 foregroundPids.insert(item.pid);
570 }
571 std::set<int32_t> tForegroundPids;
572
573 for (const auto &shortcut : shortcuts_) {
574 if (foregroundPids.find(shortcut.second.session) != foregroundPids.cend()) {
575 tForegroundPids.insert(shortcut.second.session);
576 }
577 }
578 std::ostringstream sPids;
579
580 if (auto iter = tForegroundPids.cbegin(); iter != tForegroundPids.cend()) {
581 sPids << *iter;
582 for (++iter; iter != tForegroundPids.cend(); ++iter) {
583 sPids << "," << *iter;
584 }
585 }
586 MMI_HILOGI("Foreground pids: [%{public}s]", sPids.str().c_str());
587 return tForegroundPids;
588 }
589
HandleKeyDown(std::shared_ptr<KeyEvent> keyEvent)590 bool KeyShortcutManager::HandleKeyDown(std::shared_ptr<KeyEvent> keyEvent)
591 {
592 bool handled = false;
593 std::set<int32_t> foregroundPids = GetForegroundPids();
594
595 for (auto &item : shortcuts_) {
596 KeyShortcut &shortcut = item.second;
597 if (shortcut.triggerType != SHORTCUT_TRIGGER_TYPE_DOWN) {
598 continue;
599 }
600 if (!foregroundPids.empty() &&
601 (foregroundPids.find(shortcut.session) == foregroundPids.cend())) {
602 continue;
603 }
604 if (!CheckCombination(keyEvent, shortcut)) {
605 continue;
606 }
607 MMI_HILOGI("Matched shortcut[No.%{public}d]"
608 "(0x%{public}x,%{public}d,%{public}d,%{public}d,SESSION:%{public}d)",
609 item.first, shortcut.modifiers, shortcut.finalKey, shortcut.longPressTime,
610 shortcut.triggerType, shortcut.session);
611 TriggerDown(keyEvent, item.first, shortcut);
612 handled = true;
613 }
614 return handled;
615 }
616
HandleKeyUp(std::shared_ptr<KeyEvent> keyEvent)617 bool KeyShortcutManager::HandleKeyUp(std::shared_ptr<KeyEvent> keyEvent)
618 {
619 bool handled = false;
620 std::set<int32_t> foregroundPids = GetForegroundPids();
621
622 for (auto &item : shortcuts_) {
623 KeyShortcut &shortcut = item.second;
624 if (shortcut.triggerType != SHORTCUT_TRIGGER_TYPE_UP) {
625 continue;
626 }
627 if (!foregroundPids.empty() &&
628 (foregroundPids.find(shortcut.session) == foregroundPids.cend())) {
629 continue;
630 }
631 if (!CheckCombination(keyEvent, shortcut)) {
632 continue;
633 }
634 MMI_HILOGI("Matched shortcut(0x%{public}x,%{public}d,%{public}d,%{public}d,SESSION:%{public}d)",
635 shortcut.modifiers, shortcut.finalKey, shortcut.longPressTime, shortcut.triggerType, shortcut.session);
636 TriggerUp(keyEvent, item.first, shortcut);
637 handled = true;
638 }
639 return handled;
640 }
641
HandleKeyCancel(std::shared_ptr<KeyEvent> keyEvent)642 bool KeyShortcutManager::HandleKeyCancel(std::shared_ptr<KeyEvent> keyEvent)
643 {
644 ResetAll();
645 return false;
646 }
647
CheckCombination(std::shared_ptr<KeyEvent> keyEvent,const KeyShortcut & shortcut) const648 bool KeyShortcutManager::CheckCombination(std::shared_ptr<KeyEvent> keyEvent, const KeyShortcut &shortcut) const
649 {
650 return (((shortcut.finalKey == SHORTCUT_PURE_MODIFIERS) && CheckPureModifiers(keyEvent, shortcut)) ||
651 ((shortcut.finalKey == keyEvent->GetKeyCode()) && CheckModifiers(keyEvent, shortcut)));
652 }
653
CheckPureModifiers(std::shared_ptr<KeyEvent> keyEvent,const KeyShortcut & shortcut) const654 bool KeyShortcutManager::CheckPureModifiers(std::shared_ptr<KeyEvent> keyEvent, const KeyShortcut &shortcut) const
655 {
656 auto iter = modifiers_.find(keyEvent->GetKeyCode());
657 if (iter == modifiers_.cend()) {
658 return false;
659 }
660 uint32_t modifiers = (shortcut.modifiers & ~iter->second);
661 auto pressedKeys = keyEvent->GetPressedKeys();
662
663 for (auto keyCode : pressedKeys) {
664 if (auto iter = modifiers_.find(keyCode); iter != modifiers_.cend()) {
665 modifiers &= ~iter->second;
666 }
667 }
668 return (modifiers == 0U);
669 }
670
CheckModifiers(std::shared_ptr<KeyEvent> keyEvent,const KeyShortcut & shortcut) const671 bool KeyShortcutManager::CheckModifiers(std::shared_ptr<KeyEvent> keyEvent, const KeyShortcut &shortcut) const
672 {
673 uint32_t modifiers = shortcut.modifiers;
674 auto pressedKeys = keyEvent->GetPressedKeys();
675
676 for (auto keyCode : pressedKeys) {
677 if (auto iter = modifiers_.find(keyCode); iter != modifiers_.cend()) {
678 modifiers &= ~iter->second;
679 }
680 }
681 return (modifiers == 0U);
682 }
683
TriggerDown(std::shared_ptr<KeyEvent> keyEvent,int32_t shortcutId,const KeyShortcut & shortcut)684 void KeyShortcutManager::TriggerDown(
685 std::shared_ptr<KeyEvent> keyEvent, int32_t shortcutId, const KeyShortcut &shortcut)
686 {
687 if (shortcut.longPressTime <= 0) {
688 MMI_HILOGI("Run shortcut[No.%{public}d]", shortcutId);
689 if (shortcut.callback != nullptr) {
690 shortcut.callback(keyEvent);
691 }
692 } else {
693 if (triggering_.find(shortcutId) != triggering_.cend()) {
694 MMI_HILOGI("Shortcut[No.%{public}d]"
695 "(0x%{public}x,%{public}d,%{public}d,%{public}d,SESSION:%{public}d) is pending",
696 shortcutId, shortcut.modifiers, shortcut.finalKey, shortcut.longPressTime,
697 shortcut.triggerType, shortcut.session);
698 return;
699 }
700 auto timerId = TimerMgr->AddTimer(shortcut.longPressTime, REPEAT_ONCE,
701 [this, tKeyEvent = KeyEvent::Clone(keyEvent), shortcutId]() {
702 triggering_.erase(shortcutId);
703 RunShortcut(tKeyEvent, shortcutId);
704 });
705 if (timerId < 0) {
706 MMI_HILOGE("AddTimer fail");
707 return;
708 }
709 triggering_.emplace(shortcutId, timerId);
710 }
711 }
712
RunShortcut(std::shared_ptr<KeyEvent> keyEvent,int32_t shortcutId)713 void KeyShortcutManager::RunShortcut(std::shared_ptr<KeyEvent> keyEvent, int32_t shortcutId)
714 {
715 if (auto iter = shortcuts_.find(shortcutId); iter != shortcuts_.end()) {
716 std::set<int32_t> foregroundPids = GetForegroundPids();
717 if (!foregroundPids.empty() &&
718 (foregroundPids.find(iter->second.session) == foregroundPids.cend())) {
719 MMI_HILOGI("Session(%{public}d) is not foreground, skip running shortcut[%{public}d]",
720 iter->second.session, shortcutId);
721 return;
722 }
723 MMI_HILOGI("Run shortcut[No.%{public}d]", shortcutId);
724 if (iter->second.callback != nullptr) {
725 iter->second.callback(keyEvent);
726 }
727 }
728 }
729
TriggerUp(std::shared_ptr<KeyEvent> keyEvent,int32_t shortcutId,const KeyShortcut & shortcut)730 void KeyShortcutManager::TriggerUp(
731 std::shared_ptr<KeyEvent> keyEvent, int32_t shortcutId, const KeyShortcut &shortcut)
732 {
733 if (shortcut.longPressTime > 0) {
734 std::optional<KeyEvent::KeyItem> keyItem = keyEvent->GetKeyItem();
735 if (!keyItem) {
736 MMI_HILOGE("Corrupted key event");
737 return;
738 }
739 auto upTime = keyEvent->GetActionTime();
740 auto downTime = keyItem->GetDownTime();
741 if (upTime - downTime < MS2US(shortcut.longPressTime)) {
742 MMI_HILOGE("upTime - downTime < duration");
743 return;
744 }
745 }
746 if (shortcut.callback != nullptr) {
747 shortcut.callback(keyEvent);
748 }
749 }
750
ResetAll()751 void KeyShortcutManager::ResetAll()
752 {
753 for (auto &item : triggering_) {
754 TimerMgr->RemoveTimer(item.second);
755 }
756 triggering_.clear();
757 }
758
ResetCheckState()759 void KeyShortcutManager::ResetCheckState()
760 {
761 isCheckShortcut_ = true;
762 }
763
764 static const std::vector<int32_t> specialKeyCodes = {
765 KeyEvent::KEYCODE_ALT_LEFT,
766 KeyEvent::KEYCODE_ALT_RIGHT,
767 KeyEvent::KEYCODE_TAB,
768 KeyEvent::KEYCODE_VOLUME_UP,
769 KeyEvent::KEYCODE_VOLUME_DOWN,
770 KeyEvent::KEYCODE_POWER,
771 KeyEvent::KEYCODE_HEADSETHOOK,
772 KeyEvent::KEYCODE_HOME,
773 KeyEvent::KEYCODE_MENU,
774 KeyEvent::KEYCODE_VOICE_ASSISTANT
775 };
776
IsCheckUpShortcut(const std::shared_ptr<KeyEvent> & keyEvent)777 bool KeyShortcutManager::IsCheckUpShortcut(const std::shared_ptr<KeyEvent> &keyEvent)
778 {
779 auto it = std::find(specialKeyCodes.begin(), specialKeyCodes.end(), keyEvent->GetKeyCode());
780 if (it != specialKeyCodes.end() && keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_UP) {
781 return true;
782 }
783 if (isCheckShortcut_) {
784 isCheckShortcut_ = false;
785 return true;
786 }
787 return false;
788 }
789
HaveShortcutConsumed(std::shared_ptr<KeyEvent> keyEvent)790 bool KeyShortcutManager::HaveShortcutConsumed(std::shared_ptr<KeyEvent> keyEvent)
791 {
792 auto it = std::find(specialKeyCodes.begin(), specialKeyCodes.end(), keyEvent->GetKeyCode());
793 if (it != specialKeyCodes.end() && keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_UP) {
794 return false;
795 }
796 return (shortcutConsumed_.find(keyEvent->GetKeyCode()) != shortcutConsumed_.cend());
797 }
798
UpdateShortcutConsumed(std::shared_ptr<KeyEvent> keyEvent)799 void KeyShortcutManager::UpdateShortcutConsumed(std::shared_ptr<KeyEvent> keyEvent)
800 {
801 if (keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_UP) {
802 shortcutConsumed_.erase(keyEvent->GetKeyCode());
803 }
804 }
805
MarkShortcutConsumed(const ShortcutKey & shortcut)806 void KeyShortcutManager::MarkShortcutConsumed(const ShortcutKey &shortcut)
807 {
808 std::for_each(shortcut.preKeys.cbegin(), shortcut.preKeys.cend(),
809 [this](auto keyCode) {
810 shortcutConsumed_.emplace(keyCode);
811 });
812 if (shortcut.triggerType == KeyEvent::KEY_ACTION_DOWN) {
813 shortcutConsumed_.emplace(shortcut.finalKey);
814 }
815 isCheckShortcut_ = false;
816 }
817
MarkShortcutConsumed(const KeyOption & shortcut)818 void KeyShortcutManager::MarkShortcutConsumed(const KeyOption &shortcut)
819 {
820 auto preKeys = shortcut.GetPreKeys();
821
822 std::for_each(preKeys.cbegin(), preKeys.cend(),
823 [this](auto keyCode) {
824 shortcutConsumed_.emplace(keyCode);
825 });
826 if (shortcut.IsFinalKeyDown()) {
827 shortcutConsumed_.emplace(shortcut.GetFinalKey());
828 }
829 shortcutConsumed_.erase(KeyEvent::KEYCODE_VOLUME_UP);
830 shortcutConsumed_.erase(KeyEvent::KEYCODE_VOLUME_DOWN);
831 shortcutConsumed_.erase(KeyEvent::KEYCODE_POWER);
832 isCheckShortcut_ = false;
833 }
834
ResetTriggering(std::shared_ptr<KeyEvent> keyEvent)835 void KeyShortcutManager::ResetTriggering(std::shared_ptr<KeyEvent> keyEvent)
836 {
837 if (keyEvent->GetKeyAction() == KeyEvent::KEY_ACTION_DOWN) {
838 for (auto iter = triggering_.cbegin(); iter != triggering_.cend();) {
839 auto shortcutIter = shortcuts_.find(iter->first);
840 if ((shortcutIter != shortcuts_.cend()) &&
841 WillResetOnKeyDown(keyEvent->GetKeyCode(), shortcutIter->second)) {
842 MMI_HILOGI("Reset triggering shortcut[%{public}d]", iter->first);
843 TimerMgr->RemoveTimer(iter->second);
844 iter = triggering_.erase(iter);
845 } else {
846 ++iter;
847 }
848 }
849 } else {
850 for (auto iter = triggering_.cbegin(); iter != triggering_.cend();) {
851 auto shortcutIter = shortcuts_.find(iter->first);
852 if ((shortcutIter != shortcuts_.cend()) &&
853 WillResetOnKeyUp(keyEvent->GetKeyCode(), shortcutIter->second)) {
854 MMI_HILOGI("Reset triggering shortcut[%{public}d]", iter->first);
855 TimerMgr->RemoveTimer(iter->second);
856 iter = triggering_.erase(iter);
857 } else {
858 ++iter;
859 }
860 }
861 }
862 }
863
WillResetOnKeyDown(int32_t keyCode,const KeyShortcut & shortcut) const864 bool KeyShortcutManager::WillResetOnKeyDown(int32_t keyCode, const KeyShortcut &shortcut) const
865 {
866 if (keyCode == shortcut.finalKey) {
867 return false;
868 }
869 auto modIter = modifiers_.find(keyCode);
870 return ((modIter == modifiers_.cend()) || ((modIter->second & shortcut.modifiers) == 0U));
871 }
872
WillResetOnKeyUp(int32_t keyCode,const KeyShortcut & shortcut) const873 bool KeyShortcutManager::WillResetOnKeyUp(int32_t keyCode, const KeyShortcut &shortcut) const
874 {
875 if (keyCode == shortcut.finalKey) {
876 return true;
877 }
878 auto modIter = modifiers_.find(keyCode);
879 return ((modIter != modifiers_.cend()) && ((modIter->second & shortcut.modifiers) != 0U));
880 }
881
ResetTriggering(int32_t shortcutId)882 void KeyShortcutManager::ResetTriggering(int32_t shortcutId)
883 {
884 if (auto iter = triggering_.find(shortcutId); iter != triggering_.cend()) {
885 TimerMgr->RemoveTimer(iter->second);
886 triggering_.erase(iter);
887 }
888 }
889
GetAllSystemHotkeys(std::vector<std::unique_ptr<KeyOption>> & sysKeys)890 int32_t KeyShortcutManager::GetAllSystemHotkeys(std::vector<std::unique_ptr<KeyOption>> &sysKeys)
891 {
892 CALL_DEBUG_ENTER;
893 for (const auto &item : hotkeys_) {
894 std::unique_ptr<KeyOption> keyOptionPtr = std::make_unique<KeyOption>();
895 keyOptionPtr->SetPreKeys(item.preKeys);
896 keyOptionPtr->SetFinalKey(item.finalKey);
897 sysKeys.push_back(std::move(keyOptionPtr));
898 }
899 return RET_OK;
900 }
901
LoadHotkeys()902 void KeyShortcutManager::LoadHotkeys()
903 {
904 char cfgName[] { "etc/multimodalinput/system_hotkeys_config.json" };
905 char buf[MAX_PATH_LEN] {};
906 char *cfgPath = ::GetOneCfgFile(cfgName, buf, sizeof(buf));
907
908 if ((cfgPath == nullptr) || (cfgPath[0] == '\0') || (strlen(cfgPath) > MAX_PATH_LEN)) {
909 MMI_HILOGE("No '%{public}s' was found", cfgPath);
910 return;
911 }
912 MMI_HILOGI("Config of hotkey:%{public}s", cfgPath);
913 ReadHotkeys(std::string(cfgPath));
914 }
915
ReadHotkeys(const std::string & cfgPath)916 void KeyShortcutManager::ReadHotkeys(const std::string &cfgPath)
917 {
918 std::string cfg = ReadJsonFile(cfgPath);
919 JsonParser parser;
920 parser.json_ = cJSON_Parse(cfg.c_str());
921 if (!cJSON_IsObject(parser.json_)) {
922 MMI_HILOGE("Not json format");
923 return;
924 }
925 cJSON* jsonHotkeys = cJSON_GetObjectItemCaseSensitive(parser.json_, "Hotkeys");
926 if (!jsonHotkeys) {
927 MMI_HILOGE("JsonHotkeys is nullptr");
928 return;
929 }
930 if (!cJSON_IsArray(jsonHotkeys)) {
931 MMI_HILOGE("JsonHotkeys is not array");
932 return;
933 }
934 int32_t nSysKeys = cJSON_GetArraySize(jsonHotkeys);
935 for (int32_t index = 0; index < nSysKeys; ++index) {
936 cJSON *jsonHotkey = cJSON_GetArrayItem(jsonHotkeys, index);
937 if (!jsonHotkey) {
938 MMI_HILOGE("JsonHotkey is nullptr");
939 return;
940 }
941 if (ReadHotkey(jsonHotkey) != RET_OK) {
942 MMI_HILOGE("Read hotkey failed");
943 return;
944 }
945 }
946 }
947
ReadHotkey(cJSON * jsonHotkey)948 int32_t KeyShortcutManager::ReadHotkey(cJSON *jsonHotkey)
949 {
950 if (!cJSON_IsObject(jsonHotkey)) {
951 MMI_HILOGE("Not json object");
952 return RET_ERR;
953 }
954 cJSON *jsonPreKeys = cJSON_GetObjectItem(jsonHotkey, "preKeys");
955 if (!jsonPreKeys) {
956 MMI_HILOGE("JsonPreKeys is nullptr");
957 return RET_ERR;
958 }
959 if (!cJSON_IsArray(jsonPreKeys)) {
960 MMI_HILOGE("Expect array for PreKeys");
961 return RET_ERR;
962 }
963 std::set<int32_t> preKeys;
964 int32_t nPreKeys = cJSON_GetArraySize(jsonPreKeys);
965
966 for (int32_t index = 0; index < nPreKeys; ++index) {
967 cJSON *jsonPreKey = cJSON_GetArrayItem(jsonPreKeys, index);
968 if (!cJSON_IsNumber(jsonPreKey)) {
969 MMI_HILOGE("Expect number for PreKey");
970 return RET_ERR;
971 }
972 preKeys.insert(static_cast<int32_t>(cJSON_GetNumberValue(jsonPreKey)));
973 }
974 cJSON *jsonFinalKey = cJSON_GetObjectItem(jsonHotkey, "finalKey");
975 if (!jsonFinalKey) {
976 MMI_HILOGE("JsonFinalKey is nullptr");
977 return RET_ERR;
978 }
979 if (!cJSON_IsNumber(jsonFinalKey)) {
980 MMI_HILOGE("Expect number for FinalKey");
981 return RET_ERR;
982 }
983 int32_t finalKey = static_cast<int32_t>(cJSON_GetNumberValue(jsonFinalKey));
984 return AddHotkey(preKeys, finalKey);
985 }
986
AddHotkey(const std::set<int32_t> & preKeys,int32_t finalKey)987 int32_t KeyShortcutManager::AddHotkey(const std::set<int32_t> &preKeys, int32_t finalKey)
988 {
989 SystemHotkey hotkey {
990 .preKeys = preKeys,
991 .finalKey = finalKey,
992 };
993 for (const auto &item : hotkey.preKeys) {
994 if ((modifiers_.find(item) == modifiers_.cend()) && (item != KeyEvent::KEYCODE_SYSRQ)) {
995 MMI_HILOGE("Not hotkeys");
996 return RET_ERR;
997 }
998 }
999
1000 if (IsModifier(hotkey.finalKey)) {
1001 MMI_HILOGE("FinalKey is modifier");
1002 return RET_ERR;
1003 }
1004 hotkeys_.emplace(hotkey);
1005 return RET_OK;
1006 }
1007 } // namespace MMI
1008 } // namespace OHOS
1009