1/*
2 * Copyright (c) 2023 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 "virtual_keyboard_builder.h"
17
18#include <getopt.h>
19#include <fstream>
20#include <iostream>
21
22#include "devicestatus_define.h"
23#include "fi_log.h"
24#include "utility.h"
25#include "virtual_keyboard.h"
26
27#undef LOG_TAG
28#define LOG_TAG "VirtualKeyboardBuilder"
29
30namespace OHOS {
31namespace Msdp {
32namespace DeviceStatus {
33namespace {
34constexpr int32_t MAXIMUM_LEVEL_ALLOWED { 3 };
35constexpr ssize_t MAXIMUM_FILESIZE_ALLOWED { 0x100000 };
36} // namespace
37
38VirtualKeyboardBuilder::VirtualKeyboardBuilder() : VirtualDeviceBuilder(GetDeviceName(), BUS_USB, 0x24ae, 0x4035)
39{
40    eventTypes_ = { EV_KEY, EV_MSC, EV_LED, EV_REP };
41    miscellaneous_ = { MSC_SCAN };
42    leds_ = { LED_NUML, LED_CAPSL, LED_SCROLLL, LED_COMPOSE, LED_KANA };
43    repeats_ = { REP_DELAY, REP_PERIOD };
44    keys_ = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
45        21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
46        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
47        61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
48        81, 82, 83, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 102,
49        103, 104, 105, 106, 107, 108, 109, 110, 111, 113, 114, 115, 116, 117, 119, 121, 122, 123, 124, 125,
50        126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 140, 142, 150, 152, 158, 159, 161,
51        163, 164, 165, 166, 173, 176, 177, 178, 179, 180, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
52        193, 194, 240, 211, 213, 214, 215, 218, 220, 221, 222, 223, 226, 227, 231, 232, 233, 236, 237, 238,
53        239, 242, 243, 245, 246, 247, 248, 464, 522, 523, 141, 145, 146, 147, 148, 149, 151, 153, 154, 157,
54        160, 162, 170, 175, 182, 200, 201, 202, 203, 204, 205, 101, 112, 118, 120 };
55}
56
57std::string VirtualKeyboardBuilder::GetDeviceName()
58{
59    return std::string("Virtual Keyboard");
60}
61
62void VirtualKeyboardBuilder::ShowUsage()
63{
64    std::cout << "Usage: vdevadm act -t K [-d <key>] [-u <key>] [-w <ms>] [-f <FILE>] [-r <FILE>]" << std::endl;
65    std::cout << "      -d <key>    Down <key>" << std::endl;
66    std::cout << "      -u <key>    Release <key>" << std::endl;
67    std::cout << "      -w <ms>     Wait for <ms> milliseconds." << std::endl;
68    std::cout << "      -f <FILE>   Read actions from <FILE>" << std::endl;
69    std::cout << "      -r <FILE>   Read raw input data from <FILE>." << std::endl;
70    std::cout << std::endl;
71}
72
73void VirtualKeyboardBuilder::Mount()
74{
75    CALL_DEBUG_ENTER;
76    std::cout << "Start to mount virtual keyboard." << std::endl;
77    if (VirtualKeyboard::GetDevice() != nullptr) {
78        std::cout << "Virtual keyboard has been mounted." << std::endl;
79        return;
80    }
81    VirtualKeyboardBuilder vKeyboard;
82    if (!vKeyboard.SetUp()) {
83        std::cout << "Failed to mount virtual keyboard." << std::endl;
84        return;
85    }
86
87    int32_t nTries = 6;
88    do {
89        std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));
90    } while ((nTries-- > 0) && (VirtualKeyboard::GetDevice() == nullptr));
91    if (VirtualKeyboard::GetDevice() == nullptr) {
92        std::cout << "Failed to mount virtual keyboard." << std::endl;
93        return;
94    }
95
96    std::cout << "Mount virtual keyboard successfully." << std::endl;
97    VirtualDeviceBuilder::Daemonize();
98
99    for (;;) {
100        std::this_thread::sleep_for(std::chrono::minutes(1));
101    }
102}
103
104void VirtualKeyboardBuilder::Unmount()
105{
106    CALL_DEBUG_ENTER;
107    VirtualDeviceBuilder::Unmount("keyboard", "K");
108}
109
110void VirtualKeyboardBuilder::Clone()
111{
112    CALL_DEBUG_ENTER;
113    if (VirtualKeyboard::GetDevice() != nullptr) {
114        std::cout << "Virtual keyboard has been mounted" << std::endl;
115        return;
116    }
117
118    std::vector<std::shared_ptr<VirtualDevice>> vDevs;
119    int32_t ret = VirtualDeviceBuilder::ScanFor(
120        [](std::shared_ptr<VirtualDevice> vDev) { return ((vDev != nullptr) && vDev->IsKeyboard()); }, vDevs);
121    if (ret != RET_OK) {
122        std::cout << "Failed while scanning for keyboard" << std::endl;
123        return;
124    }
125    auto vDev = VirtualDeviceBuilder::Select(vDevs, "keyboard");
126    CHKPV(vDev);
127    std::cout << "Cloning \'" << vDev->GetName() << "\'." << std::endl;
128    VirtualDeviceBuilder vBuilder(GetDeviceName(), vDev);
129    if (!vBuilder.SetUp()) {
130        std::cout << "Failed to clone \' " << vDev->GetName() << " \'." << std::endl;
131        return;
132    }
133
134    int32_t nTries = 3;
135    do {
136        std::this_thread::sleep_for(std::chrono::seconds(1));
137    } while ((nTries-- > 0) && (VirtualKeyboard::GetDevice() == nullptr));
138    if (VirtualKeyboard::GetDevice() == nullptr) {
139        std::cout << "Failed to clone \' " << vDev->GetName() << " \'." << std::endl;
140        return;
141    }
142
143    std::cout << "Clone \'" << vDev->GetName() << "\' successfully" << std::endl;
144    VirtualDeviceBuilder::Daemonize();
145    for (;;) {
146        std::this_thread::sleep_for(std::chrono::minutes(1));
147    }
148}
149
150void VirtualKeyboardBuilder::Act(int32_t argc, char *argv[])
151{
152    CALL_DEBUG_ENTER;
153    int32_t opt = getopt(argc, argv, "d:u:f:r:w:");
154    if (opt < 0) {
155        std::cout << "Vdevadm act: required option is missing" << std::endl;
156        ShowUsage();
157        return;
158    }
159    if (VirtualKeyboard::GetDevice() == nullptr) {
160        std::cout << "No virtual keyboard." << std::endl;
161        return;
162    }
163    do {
164        switch (opt) {
165            case 'd': {
166                ReadDownAction();
167                break;
168            }
169            case 'u': {
170                ReadUpAction();
171                break;
172            }
173            case 'f': {
174                ReadActions(optarg);
175                break;
176            }
177            case 'r': {
178                ReadRawInput(optarg);
179                break;
180            }
181            case 'w': {
182                VirtualDeviceBuilder::WaitFor(optarg, "keyboard");
183                break;
184            }
185            default: {
186                ShowUsage();
187                break;
188            }
189        }
190    } while ((opt = getopt(argc, argv, "d:u:f:r:w:")) >= 0);
191}
192
193void VirtualKeyboardBuilder::ReadDownAction()
194{
195    CALL_DEBUG_ENTER;
196    CHKPV(optarg);
197    if (!Utility::IsInteger(optarg)) {
198        std::cout << "Require arguments for Option \'-d\'." << std::endl;
199        ShowUsage();
200        return;
201    }
202
203    int32_t key = std::atoi(optarg);
204    std::cout << "[keyboard] down key: [" << key << "]" << std::endl;
205    VirtualKeyboard::GetDevice()->Down(key);
206}
207
208void VirtualKeyboardBuilder::ReadUpAction()
209{
210    CALL_DEBUG_ENTER;
211    CHKPV(optarg);
212    if (!Utility::IsInteger(optarg)) {
213        std::cout << "Require arguments for Option \'-u\'." << std::endl;
214        ShowUsage();
215        return;
216    }
217
218    int32_t key = std::atoi(optarg);
219    std::cout << "[keyboard] release key: [" << key << "]" << std::endl;
220    VirtualKeyboard::GetDevice()->Up(key);
221}
222
223void VirtualKeyboardBuilder::ReadActions(const char *path)
224{
225    CALL_DEBUG_ENTER;
226    CHKPV(path);
227    char realPath[PATH_MAX] {};
228    if (realpath(path, realPath) == nullptr) {
229        std::cout << "[keyboard] an invalid path: " << path << std::endl;
230        return;
231    }
232    if (Utility::GetFileSize(realPath) > MAXIMUM_FILESIZE_ALLOWED) {
233        std::cout << "[keyboard] the file size is too large" << std::endl;
234        return;
235    }
236    json model;
237    int32_t ret = VirtualDeviceBuilder::ReadFile(realPath, model);
238    if (ret == RET_ERR) {
239        FI_HILOGE("Failed to read the file");
240        return;
241    }
242    ReadModel(model, MAXIMUM_LEVEL_ALLOWED);
243}
244
245void VirtualKeyboardBuilder::ReadModel(const nlohmann::json &model, int32_t level)
246{
247    CALL_DEBUG_ENTER;
248    if (!model.is_object() && !model.is_array()) {
249        FI_HILOGE("model is not an array or object");
250        return;
251    }
252    if (model.is_object()) {
253        auto tIter = model.find("actions");
254        if (tIter != model.cend() && tIter->is_array()) {
255            std::for_each(tIter->cbegin(), tIter->cend(), [](const auto &item) { ReadAction(item); });
256        }
257    }
258    if (model.is_array() && level > 0) {
259        for (const auto &m : model) {
260            ReadModel(m, level - 1);
261        }
262    }
263}
264
265void VirtualKeyboardBuilder::ReadAction(const nlohmann::json &model)
266{
267    CALL_DEBUG_ENTER;
268    if (!model.is_object()) {
269        FI_HILOGD("Not an object");
270        return;
271    }
272    auto it = model.find("action");
273    if (it != model.cend() && it->is_string()) {
274        static const std::unordered_map<std::string, std::function<void(const nlohmann::json &model)>> actions {
275            { "down", &VirtualKeyboardBuilder::HandleDown },
276            { "up", &VirtualKeyboardBuilder::HandleUp },
277            { "wait", &VirtualKeyboardBuilder::HandleWait }
278        };
279        auto actionItr = actions.find(it.value());
280        if (actionItr != actions.cend()) {
281            actionItr->second(model);
282        }
283    }
284}
285
286void VirtualKeyboardBuilder::HandleDown(const nlohmann::json &model)
287{
288    CALL_DEBUG_ENTER;
289    auto it = model.find("key");
290    if (it != model.cend() && it->is_number_integer()) {
291        std::cout << "[virtual keyboard] down key: " << it.value() << std::endl;
292        VirtualKeyboard::GetDevice()->Down(it.value());
293    }
294}
295
296void VirtualKeyboardBuilder::HandleUp(const nlohmann::json &model)
297{
298    CALL_DEBUG_ENTER;
299    auto it = model.find("key");
300    if (it != model.cend() && it->is_number_integer()) {
301        std::cout << "[virtual keyboard] release key: " << it.value() << std::endl;
302        VirtualKeyboard::GetDevice()->Up(it.value());
303    }
304}
305
306void VirtualKeyboardBuilder::HandleWait(const nlohmann::json &model)
307{
308    CALL_DEBUG_ENTER;
309    auto it = model.find("duration");
310    if (it != model.cend() && it->is_number_integer()) {
311        int32_t waitTime = it.value();
312        std::cout << "[virtual keyboard] wait for " << waitTime << " milliseconds" << std::endl;
313        VirtualDeviceBuilder::WaitFor("virtual keyboard", waitTime);
314    }
315}
316
317void VirtualKeyboardBuilder::ReadRawInput(const char *path)
318{
319    CALL_DEBUG_ENTER;
320    CHKPV(path);
321    char realPath[PATH_MAX] {};
322
323    if (realpath(path, realPath) == nullptr) {
324        std::cout << "[keyboard] invalid path: " << path << std::endl;
325        return;
326    }
327    if (Utility::GetFileSize(realPath) > MAXIMUM_FILESIZE_ALLOWED) {
328        std::cout << "[keyboard] file is too large" << std::endl;
329        return;
330    }
331    json model;
332
333    int32_t ret = VirtualDeviceBuilder::ReadFile(realPath, model);
334    if (ret == RET_ERR) {
335        FI_HILOGE("Failed to read raw input data");
336        return;
337    }
338    ReadRawModel(model, MAXIMUM_LEVEL_ALLOWED);
339}
340
341void VirtualKeyboardBuilder::ReadRawModel(const nlohmann::json &model, int32_t level)
342{
343    CALL_DEBUG_ENTER;
344    if (!model.is_object() && !model.is_array()) {
345        FI_HILOGE("model is not an array or object");
346        return;
347    }
348    if (model.is_object()) {
349        auto typeIter = model.find("type");
350        if (typeIter == model.cend() || !typeIter->is_string() || (std::string(typeIter.value()).compare("raw") != 0)) {
351            std::cout << "Expect raw input data" << std::endl;
352            return;
353        }
354        auto actionIter = model.find("actions");
355        if (actionIter != model.cend() && actionIter->is_array()) {
356            std::for_each(actionIter->cbegin(), actionIter->cend(), [](const auto &item) { ReadRawData(item); });
357        }
358    }
359    if (model.is_array() && level > 0) {
360        for (const auto &m : model) {
361            ReadRawModel(m, level - 1);
362        }
363    }
364}
365
366void VirtualKeyboardBuilder::ReadRawData(const nlohmann::json &model)
367{
368    CALL_DEBUG_ENTER;
369    if (!model.is_object()) {
370        FI_HILOGE("model is not an object");
371        return;
372    }
373    auto valueIter = model.find("value");
374    if (valueIter == model.cend() || !valueIter->is_number_integer()) {
375        return;
376    }
377    auto codeIter = model.find("code");
378    if (codeIter == model.cend() || !codeIter->is_number_integer()) {
379        return;
380    }
381    auto typeIter = model.find("type");
382    if (typeIter == model.cend() || !typeIter->is_number_integer()) {
383        return;
384    }
385    std::cout << "[virtual keyboard] raw input: [" << typeIter.value() << ", " << codeIter.value() << ", " <<
386        valueIter.value() << "]" << std::endl;
387    VirtualKeyboard::GetDevice()->SendEvent(typeIter.value(), codeIter.value(), valueIter.value());
388}
389} // namespace DeviceStatus
390} // namespace Msdp
391} // namespace OHOS