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 "dp_command.h"
17 
18 #include <functional>
19 #include <getopt.h>
20 #include <istream>
21 #include <list>
22 #include <map>
23 #include <memory>
24 #include <sstream>
25 #include <streambuf>
26 #include <string>
27 #include <type_traits>
28 #include <unistd.h>
29 
30 #include "device_profile_log.h"
31 #include "distributed_device_profile_client.h"
32 #include "iprofile_event_notifier.h"
33 #include "nlohmann/json.hpp"
34 #include "nlohmann/json_fwd.hpp"
35 #include "profile_event.h"
36 #include "service_characteristic_profile.h"
37 #include "softbus_bus_center.h"
38 #include "subscribe_info.h"
39 #include "sync_options.h"
40 
41 namespace OHOS {
42 namespace DeviceProfile {
43 namespace {
44 const std::string TAG = "DpShellCommand";
45 const std::string DP_TOOL_NAME = "dp";
46 const std::string DP_HELP_MSG = "usage: dp <command> <options>\n"
47                              "These are common dp commands list:\n"
48                              "  help         list available commands\n"
49                              "  getDevice    list all devices\n"
50                              "  query        query device info with options\n"
51                              "  put          put device info with options\n"
52                              "  sync         sync device info with options\n"
53                              "  delete       delete device info with options\n"
54                              "  subscribe    subscribe device info with options\n";
55 const std::string HELP_MSG_QUERY =
56     "usage: dp query <options>\n"
57     "options list:\n"
58     "  -h, --help                               list available commands\n"
59     "  -d  <device-id>                          query device info by a device id\n"
60     "  -s  <service-id>                         query device info by a service id\n";
61 const std::string HELP_MSG_SYNC =
62     "usage: dp sync <options>\n"
63     "options list:\n"
64     "  -h, --help                               list available commands\n"
65     "  -d  <device-ids>                         sync device info by a device ids\n"
66     "  -m  <mode>                               sync device info by mode\n";
67 const std::string HELP_MSG_PUT =
68     "usage: dp put <options>\n"
69     "options list:\n"
70     "  -h, --help                               list available commands\n"
71     "  -s  <service-id>                         put device info by service id\n"
72     "  -t  <service-type>                       put device info by service type\n";
73 const std::string HELP_MSG_SUBSCRIBE =
74     "usage: dp subscribe <options>\n"
75     "options list:\n"
76     "  -h, --help                               list available commands\n"
77     "  -s  <service-ids>                        subscribe device info by service ids\n"
78     "  -d  <device-id>                          subscribe device info by device id\n";
79 const std::string HELP_MSG_DELETE =
80     "usage: dp delete <options>\n"
81     "options list:\n"
82     "  -h, --help                               list available commands\n"
83     "  -s  <service-id>                        service id to delete\n";
84 constexpr int32_t API_LEVEL = 7;
85 constexpr int32_t SYNC_SLEEP_TIME = 10;
86 constexpr int32_t BASE = 10;
87 constexpr int32_t SUBSCRIBE_SLEEP_TIME = 120;
88 const std::string SHORT_OPTIONS = "hd:s:m:t:";
89 const struct option LONG_OPTIONS[] = {
90     {"help", no_argument, nullptr, 'h'},
91     {"device-id", required_argument, nullptr, 'd'},
92     {"service-id", required_argument, nullptr, 's'},
93     {"mode", required_argument, nullptr, 'm'},
94     {"service-type", required_argument, nullptr, 't'},
95     {nullptr, nullptr, nullptr, nullptr},
96 };
97 }
98 
DpShellCommand(int argc, char *argv[])99 DpShellCommand::DpShellCommand(int argc, char *argv[]) : ShellCommand(argc, argv, DP_TOOL_NAME)
100 {
101 }
102 
init()103 ErrCode DpShellCommand::init()
104 {
105     return ERR_OK;
106 }
107 
CreateCommandMap()108 ErrCode DpShellCommand::CreateCommandMap()
109 {
110     commandMap_ = {
111         {"help", std::bind(&DpShellCommand::HelpCommand, this)},
112         {"getDevice", std::bind(&DpShellCommand::GetDeviceCommand, this)},
113         {"query", std::bind(&DpShellCommand::QueryCommand, this)},
114         {"put", std::bind(&DpShellCommand::PutCommand, this)},
115         {"delete", std::bind(&DpShellCommand::DeleteCommand, this)},
116         {"sync", std::bind(&DpShellCommand::SyncCommand, this)},
117         {"subscribe", std::bind(&DpShellCommand::SubscribeCommand, this)},
118     };
119     return ERR_OK;
120 }
121 
CreateMessageMap()122 ErrCode DpShellCommand::CreateMessageMap()
123 {
124     messageMap_ = {};
125     return ERR_OK;
126 }
127 
HelpCommand()128 ErrCode DpShellCommand::HelpCommand()
129 {
130     resultReceiver_.append(DP_HELP_MSG);
131     return ERR_OK;
132 }
133 
GetDeviceCommand()134 ErrCode DpShellCommand::GetDeviceCommand()
135 {
136     resultReceiver_.append("[remote device list]\n");
137     NodeBasicInfo *info = nullptr;
138     int32_t infoNum = 0;
139     int32_t ret = GetAllNodeDeviceInfo("dp", &info, &infoNum);
140     if (ret != ERR_OK) {
141         resultReceiver_.append("get remote device list error\n");
142         return ret;
143     }
144     for (int32_t i = 0; i < infoNum; i++) {
145         if (info == nullptr) {
146             continue;
147         }
148         resultReceiver_.append("networkId: " + std::string(info->networkId)
149             + " deviceName:" + std::string(info->deviceName) + "\n");
150         info++;
151     }
152 
153     resultReceiver_.append("[local device list]\n");
154     NodeBasicInfo localInfo;
155     ret = GetLocalNodeDeviceInfo("dp", &localInfo);
156     if (ret != ERR_OK) {
157         resultReceiver_.append("get local device error\n");
158         FreeNodeInfo(info);
159         return ret;
160     }
161     resultReceiver_.append("networkId: " + std::string(localInfo.networkId)
162         + " deviceName:" + std::string(localInfo.deviceName) + "\n");
163     FreeNodeInfo(info);
164     return ERR_OK;
165 }
166 
QueryCommand()167 ErrCode DpShellCommand::QueryCommand()
168 {
169     int32_t result = ERR_OK;
170     std::string serviceType;
171     std::string deviceId;
172     std::string serviceId;
173     while (true) {
174         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
175         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
176         if (optind < 0 || optind > argc_) {
177             return OHOS::ERR_INVALID_VALUE;
178         }
179 
180         if (option == -1) {
181             break;
182         }
183         result = HandleNormalOption(option, deviceId, serviceId, serviceType);
184     }
185 
186     if (result == ERR_OK) {
187         ServiceCharacteristicProfile profile;
188         DistributedDeviceProfileClient::GetInstance().GetDeviceProfile(deviceId, serviceId, profile);
189         std::string jsonData = profile.GetCharacteristicProfileJson();
190         resultReceiver_.append("ServiceId:" + profile.GetServiceId() + "\n");
191         resultReceiver_.append("ServiceType:" + profile.GetServiceType() + "\n");
192         resultReceiver_.append("jsonData:" + jsonData + "\n");
193     } else {
194         resultReceiver_.append(HELP_MSG_QUERY);
195     }
196     return result;
197 }
198 
PutCommand()199 ErrCode DpShellCommand::PutCommand()
200 {
201     int32_t result = ERR_OK;
202     std::string serviceType;
203     std::string serviceId;
204     std::string deviceId;
205     while (true) {
206         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
207         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
208         if (optind < 0 || optind > argc_) {
209             return OHOS::ERR_INVALID_VALUE;
210         }
211         if (option == -1) {
212             break;
213         }
214         result = HandleNormalOption(option, deviceId, serviceId, serviceType);
215     }
216 
217     if (result == ERR_OK) {
218         ServiceCharacteristicProfile profile;
219         profile.SetServiceId(serviceId);
220         profile.SetServiceType(serviceType);
221         nlohmann::json j;
222         j["testVersion"] = "3.0.0";
223         j["testApiLevel"] = API_LEVEL;
224         profile.SetCharacteristicProfileJson(j.dump());
225         DistributedDeviceProfileClient::GetInstance().PutDeviceProfile(profile);
226     } else {
227         resultReceiver_.append(HELP_MSG_PUT);
228     }
229     return result;
230 }
231 
DeleteCommand()232 ErrCode DpShellCommand::DeleteCommand()
233 {
234     int32_t result = ERR_OK;
235     std::string serviceType;
236     std::string deviceId;
237     std::string serviceId;
238 
239     while (true) {
240         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
241         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
242         if (optind < 0 || optind > argc_) {
243             return OHOS::ERR_INVALID_VALUE;
244         }
245 
246         if (option == -1) {
247             break;
248         }
249         result = HandleNormalOption(option, deviceId, serviceId, serviceType);
250     }
251 
252     if (result == ERR_OK) {
253         DistributedDeviceProfileClient::GetInstance().DeleteDeviceProfile(serviceId);
254     } else {
255         resultReceiver_.append(HELP_MSG_DELETE);
256     }
257     return result;
258 }
259 
SyncCommand()260 ErrCode DpShellCommand::SyncCommand()
261 {
262     int32_t result = ERR_OK;
263     std::list<std::string> deviceIds;
264     std::string modeStr;
265     while (true) {
266         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
267         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
268         if (optind < 0 || optind > argc_) {
269             return OHOS::ERR_INVALID_VALUE;
270         }
271 
272         if (option == -1) {
273             break;
274         }
275         result = HandleSyncOption(option, modeStr, deviceIds);
276     }
277 
278     if (result == ERR_OK) {
279         SyncOptions syncOption;
280         int64_t mode = strtol(modeStr.c_str(), nullptr, BASE);
281         syncOption.SetSyncMode((OHOS::DeviceProfile::SyncMode)mode);
282         for (const auto& deviceId : deviceIds) {
283             syncOption.AddDevice(deviceId);
284         }
285         DistributedDeviceProfileClient::GetInstance().SyncDeviceProfile(syncOption,
286             std::make_shared<ProfileEventCallback>());
287         sleep(SYNC_SLEEP_TIME);
288         HILOGI("sync end");
289     } else {
290         resultReceiver_.append(HELP_MSG_SYNC);
291     }
292     return result;
293 }
294 
SubscribeCommand()295 ErrCode DpShellCommand::SubscribeCommand()
296 {
297     int32_t result = ERR_OK;
298     std::list<std::string> serviceIds;
299     std::string deviceId;
300     while (true) {
301         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
302         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
303         if (optind < 0 || optind > argc_) {
304             return OHOS::ERR_INVALID_VALUE;
305         }
306 
307         if (option == -1) {
308             break;
309         }
310         result = HandleSubscribeOption(option, deviceId, serviceIds);
311     }
312 
313     if (result == ERR_OK) {
314         auto callback = std::make_shared<ProfileEventCallback>();
315         std::list<SubscribeInfo> subscribeInfos;
316         ExtraInfo extraInfo;
317         extraInfo["deviceId"] = deviceId;
318         extraInfo["serviceIds"] = serviceIds;
319 
320         SubscribeInfo info1;
321         info1.profileEvent = ProfileEvent::EVENT_PROFILE_CHANGED;
322         info1.extraInfo = std::move(extraInfo);
323         subscribeInfos.emplace_back(info1);
324 
325         SubscribeInfo info2;
326         info2.profileEvent = ProfileEvent::EVENT_SYNC_COMPLETED;
327         subscribeInfos.emplace_back(info2);
328 
329         std::list<ProfileEvent> failedEvents;
330         DistributedDeviceProfileClient::GetInstance().SubscribeProfileEvents(subscribeInfos, callback, failedEvents);
331         sleep(SUBSCRIBE_SLEEP_TIME);
332         std::list<ProfileEvent> profileEvents;
333         profileEvents.emplace_back(ProfileEvent::EVENT_PROFILE_CHANGED);
334         failedEvents.clear();
335         DistributedDeviceProfileClient::GetInstance().UnsubscribeProfileEvents(profileEvents, callback, failedEvents);
336         sleep(SYNC_SLEEP_TIME);
337     } else {
338         resultReceiver_.append(HELP_MSG_SUBSCRIBE);
339     }
340     return result;
341 }
342 
HandleNormalOption(int option, std::string& deviceId, std::string& serviceId, std::string& serviceType)343 int32_t DpShellCommand::HandleNormalOption(int option, std::string& deviceId,
344     std::string& serviceId, std::string& serviceType)
345 {
346     HILOGI("%{public}s start, option: %{public}d", __func__, option);
347     int32_t result = ERR_OK;
348     switch (option) {
349         case 'h': {
350             if (optind - 1 < argc_) {
351                 HILOGI("'dp query' %{public}s", argv_[optind - 1]);
352             }
353             result = OHOS::ERR_INVALID_VALUE;
354             break;
355         }
356         case 'd': {
357             if (optarg == nullptr) {
358                 resultReceiver_.append("error: option, requires a value\n");
359                 result = OHOS::ERR_INVALID_VALUE;
360                 break;
361             }
362             deviceId = optarg;
363             break;
364         }
365         case 's': {
366             if (optarg == nullptr) {
367                 resultReceiver_.append("error: option, requires a value\n");
368                 result = OHOS::ERR_INVALID_VALUE;
369                 break;
370             }
371             serviceId = optarg;
372             break;
373         }
374         case 't': {
375             if (optarg == nullptr) {
376                 resultReceiver_.append("error: option, requires a value\n");
377                 result = OHOS::ERR_INVALID_VALUE;
378                 break;
379             }
380             serviceType = optarg;
381             break;
382         }
383         default: {
384             result = OHOS::ERR_INVALID_VALUE;
385             HILOGE("'dp query' invalid option.");
386             break;
387         }
388     }
389     return result;
390 }
391 
HandleSyncOption(int option, std::string& mode, std::list<std::string>& deviceIds)392 int32_t DpShellCommand::HandleSyncOption(int option, std::string& mode, std::list<std::string>& deviceIds)
393 {
394     HILOGI("%{public}s start, option: %{public}d", __func__, option);
395     int32_t result = ERR_OK;
396     switch (option) {
397         case 'h': {
398             if (optind - 1 < argc_) {
399                 HILOGI("'dp sync' %{public}s", argv_[optind - 1]);
400             }
401             result = OHOS::ERR_INVALID_VALUE;
402             break;
403         }
404         case 'd': {
405             if (optarg == nullptr) {
406                 resultReceiver_.append("error: option, requires a value\n");
407                 result = OHOS::ERR_INVALID_VALUE;
408                 break;
409             }
410             std::stringstream input(optarg);
411             std::string temp;
412             while (std::getline(input, temp, ' ')) {
413                 deviceIds.push_back(temp);
414             }
415             break;
416         }
417         case 'm': {
418             if (optarg == nullptr) {
419                 resultReceiver_.append("error: option, requires a value\n");
420                 result = OHOS::ERR_INVALID_VALUE;
421                 break;
422             }
423             mode = optarg;
424             break;
425         }
426         default: {
427             result = OHOS::ERR_INVALID_VALUE;
428             HILOGE("'dp sync' invalid option.");
429             break;
430         }
431     }
432     return result;
433 }
434 
HandleSubscribeOption(int option, std::string& deviceId, std::list<std::string>& serviceIds)435 int32_t DpShellCommand::HandleSubscribeOption(int option, std::string& deviceId,
436     std::list<std::string>& serviceIds)
437 {
438     HILOGI("%{public}s start, option: %{public}d", __func__, option);
439     int32_t result = ERR_OK;
440     switch (option) {
441         case 'h': {
442             if (optind - 1 < argc_) {
443                 HILOGI("'dp subscribe' %{public}s", argv_[optind - 1]);
444             }
445             result = OHOS::ERR_INVALID_VALUE;
446             break;
447         }
448         case 's': {
449             if (optarg == nullptr) {
450                 resultReceiver_.append("error: option, requires a value\n");
451                 result = OHOS::ERR_INVALID_VALUE;
452                 break;
453             }
454             std::stringstream input(optarg);
455             std::string temp;
456             while (std::getline(input, temp, ' ')) {
457                 serviceIds.push_back(temp);
458             }
459             break;
460         }
461         case 'd': {
462             if (optarg == nullptr) {
463                 resultReceiver_.append("error: option, requires a value\n");
464                 result = OHOS::ERR_INVALID_VALUE;
465                 break;
466             }
467             deviceId = optarg;
468             break;
469         }
470         default: {
471             result = OHOS::ERR_INVALID_VALUE;
472             HILOGE("'dp subscribe' invalid option.");
473             break;
474         }
475     }
476     return result;
477 }
478 
OnSyncCompleted(const SyncResult& syncResults)479 void ProfileEventCallback::OnSyncCompleted(const SyncResult& syncResults)
480 {
481     HILOGI("OnSyncCompleted");
482 }
483 
OnProfileChanged(const ProfileChangeNotification& changeNotification)484 void ProfileEventCallback::OnProfileChanged(const ProfileChangeNotification& changeNotification)
485 {
486     HILOGI("OnProfileChanged");
487 }
488 }  // namespace DeviceProfile
489 }  // namespace OHOS