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         resultReceiver_.append("networkId: " + std::string(info->networkId)
146             + " deviceName:" + std::string(info->deviceName) + "\n");
147         info++;
148     }
149 
150     resultReceiver_.append("[local device list]\n");
151     NodeBasicInfo localInfo;
152     ret = GetLocalNodeDeviceInfo("dp", &localInfo);
153     if (ret != ERR_OK) {
154         resultReceiver_.append("get local device error\n");
155         return ret;
156     }
157     resultReceiver_.append("networkId: " + std::string(localInfo.networkId)
158         + " deviceName:" + std::string(localInfo.deviceName) + "\n");
159     return ERR_OK;
160 }
161 
QueryCommand()162 ErrCode DpShellCommand::QueryCommand()
163 {
164     int32_t result = ERR_OK;
165     std::string serviceType;
166     std::string deviceId;
167     std::string serviceId;
168     while (true) {
169         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
170         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
171         if (optind < 0 || optind > argc_) {
172             return OHOS::ERR_INVALID_VALUE;
173         }
174 
175         if (option == -1) {
176             break;
177         }
178         result = HandleNormalOption(option, deviceId, serviceId, serviceType);
179     }
180 
181     if (result == ERR_OK) {
182         ServiceCharacteristicProfile profile;
183         DistributedDeviceProfileClient::GetInstance().GetDeviceProfile(deviceId, serviceId, profile);
184         std::string jsonData = profile.GetCharacteristicProfileJson();
185         resultReceiver_.append("ServiceId:" + profile.GetServiceId() + "\n");
186         resultReceiver_.append("ServiceType:" + profile.GetServiceType() + "\n");
187         resultReceiver_.append("jsonData:" + jsonData + "\n");
188     } else {
189         resultReceiver_.append(HELP_MSG_QUERY);
190     }
191     return result;
192 }
193 
PutCommand()194 ErrCode DpShellCommand::PutCommand()
195 {
196     int32_t result = ERR_OK;
197     std::string serviceType;
198     std::string serviceId;
199     std::string deviceId;
200     while (true) {
201         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
202         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
203         if (optind < 0 || optind > argc_) {
204             return OHOS::ERR_INVALID_VALUE;
205         }
206         if (option == -1) {
207             break;
208         }
209         result = HandleNormalOption(option, deviceId, serviceId, serviceType);
210     }
211 
212     if (result == ERR_OK) {
213         ServiceCharacteristicProfile profile;
214         profile.SetServiceId(serviceId);
215         profile.SetServiceType(serviceType);
216         nlohmann::json j;
217         j["testVersion"] = "3.0.0";
218         j["testApiLevel"] = API_LEVEL;
219         profile.SetCharacteristicProfileJson(j.dump());
220         DistributedDeviceProfileClient::GetInstance().PutDeviceProfile(profile);
221     } else {
222         resultReceiver_.append(HELP_MSG_PUT);
223     }
224     return result;
225 }
226 
DeleteCommand()227 ErrCode DpShellCommand::DeleteCommand()
228 {
229     int32_t result = ERR_OK;
230     std::string serviceType;
231     std::string deviceId;
232     std::string serviceId;
233 
234     while (true) {
235         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
236         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
237         if (optind < 0 || optind > argc_) {
238             return OHOS::ERR_INVALID_VALUE;
239         }
240 
241         if (option == -1) {
242             break;
243         }
244         result = HandleNormalOption(option, deviceId, serviceId, serviceType);
245     }
246 
247     if (result == ERR_OK) {
248         DistributedDeviceProfileClient::GetInstance().DeleteDeviceProfile(serviceId);
249     } else {
250         resultReceiver_.append(HELP_MSG_DELETE);
251     }
252     return result;
253 }
254 
SyncCommand()255 ErrCode DpShellCommand::SyncCommand()
256 {
257     int32_t result = ERR_OK;
258     std::list<std::string> deviceIds;
259     std::string modeStr;
260     while (true) {
261         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
262         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
263         if (optind < 0 || optind > argc_) {
264             return OHOS::ERR_INVALID_VALUE;
265         }
266 
267         if (option == -1) {
268             break;
269         }
270         result = HandleSyncOption(option, modeStr, deviceIds);
271     }
272 
273     if (result == ERR_OK) {
274         SyncOptions syncOption;
275         int64_t mode = strtol(modeStr.c_str(), nullptr, BASE);
276         syncOption.SetSyncMode((OHOS::DeviceProfile::SyncMode)mode);
277         for (const auto& deviceId : deviceIds) {
278             syncOption.AddDevice(deviceId);
279         }
280         DistributedDeviceProfileClient::GetInstance().SyncDeviceProfile(syncOption,
281             std::make_shared<ProfileEventCallback>());
282         sleep(SYNC_SLEEP_TIME);
283         HILOGI("sync end");
284     } else {
285         resultReceiver_.append(HELP_MSG_SYNC);
286     }
287     return result;
288 }
289 
SubscribeCommand()290 ErrCode DpShellCommand::SubscribeCommand()
291 {
292     int32_t result = ERR_OK;
293     std::list<std::string> serviceIds;
294     std::string deviceId;
295     while (true) {
296         int option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr);
297         HILOGI("option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind);
298         if (optind < 0 || optind > argc_) {
299             return OHOS::ERR_INVALID_VALUE;
300         }
301 
302         if (option == -1) {
303             break;
304         }
305         result = HandleSubscribeOption(option, deviceId, serviceIds);
306     }
307 
308     if (result == ERR_OK) {
309         auto callback = std::make_shared<ProfileEventCallback>();
310         std::list<SubscribeInfo> subscribeInfos;
311         ExtraInfo extraInfo;
312         extraInfo["deviceId"] = deviceId;
313         extraInfo["serviceIds"] = serviceIds;
314 
315         SubscribeInfo info1;
316         info1.profileEvent = ProfileEvent::EVENT_PROFILE_CHANGED;
317         info1.extraInfo = std::move(extraInfo);
318         subscribeInfos.emplace_back(info1);
319 
320         SubscribeInfo info2;
321         info2.profileEvent = ProfileEvent::EVENT_SYNC_COMPLETED;
322         subscribeInfos.emplace_back(info2);
323 
324         std::list<ProfileEvent> failedEvents;
325         DistributedDeviceProfileClient::GetInstance().SubscribeProfileEvents(subscribeInfos, callback, failedEvents);
326         sleep(SUBSCRIBE_SLEEP_TIME);
327         std::list<ProfileEvent> profileEvents;
328         profileEvents.emplace_back(ProfileEvent::EVENT_PROFILE_CHANGED);
329         failedEvents.clear();
330         DistributedDeviceProfileClient::GetInstance().UnsubscribeProfileEvents(profileEvents, callback, failedEvents);
331         sleep(SYNC_SLEEP_TIME);
332     } else {
333         resultReceiver_.append(HELP_MSG_SUBSCRIBE);
334     }
335     return result;
336 }
337 
HandleNormalOption(int option,std::string & deviceId,std::string & serviceId,std::string & serviceType)338 int32_t DpShellCommand::HandleNormalOption(int option, std::string& deviceId,
339     std::string& serviceId, std::string& serviceType)
340 {
341     HILOGI("%{public}s start, option: %{public}d", __func__, option);
342     int32_t result = ERR_OK;
343     switch (option) {
344         case 'h': {
345             HILOGI("'dp query' %{public}s", argv_[optind - 1]);
346             result = OHOS::ERR_INVALID_VALUE;
347             break;
348         }
349         case 'd': {
350             if (optarg == nullptr) {
351                 resultReceiver_.append("error: option, requires a value\n");
352                 result = OHOS::ERR_INVALID_VALUE;
353                 break;
354             }
355             deviceId = optarg;
356             break;
357         }
358         case 's': {
359             if (optarg == nullptr) {
360                 resultReceiver_.append("error: option, requires a value\n");
361                 result = OHOS::ERR_INVALID_VALUE;
362                 break;
363             }
364             serviceId = optarg;
365             break;
366         }
367         case 't': {
368             if (optarg == nullptr) {
369                 resultReceiver_.append("error: option, requires a value\n");
370                 result = OHOS::ERR_INVALID_VALUE;
371                 break;
372             }
373             serviceType = optarg;
374             break;
375         }
376         default: {
377             result = OHOS::ERR_INVALID_VALUE;
378             HILOGE("'dp query' invalid option.");
379             break;
380         }
381     }
382     return result;
383 }
384 
HandleSyncOption(int option,std::string & mode,std::list<std::string> & deviceIds)385 int32_t DpShellCommand::HandleSyncOption(int option, std::string& mode, std::list<std::string>& deviceIds)
386 {
387     HILOGI("%{public}s start, option: %{public}d", __func__, option);
388     int32_t result = ERR_OK;
389     switch (option) {
390         case 'h': {
391             HILOGI("'dp sync' %{public}s", argv_[optind - 1]);
392             result = OHOS::ERR_INVALID_VALUE;
393             break;
394         }
395         case 'd': {
396             if (optarg == nullptr) {
397                 resultReceiver_.append("error: option, requires a value\n");
398                 result = OHOS::ERR_INVALID_VALUE;
399                 break;
400             }
401             std::stringstream input(optarg);
402             std::string temp;
403             while (std::getline(input, temp, ' ')) {
404                 deviceIds.push_back(temp);
405             }
406             break;
407         }
408         case 'm': {
409             if (optarg == nullptr) {
410                 resultReceiver_.append("error: option, requires a value\n");
411                 result = OHOS::ERR_INVALID_VALUE;
412                 break;
413             }
414             mode = optarg;
415             break;
416         }
417         default: {
418             result = OHOS::ERR_INVALID_VALUE;
419             HILOGE("'dp sync' invalid option.");
420             break;
421         }
422     }
423     return result;
424 }
425 
HandleSubscribeOption(int option,std::string & deviceId,std::list<std::string> & serviceIds)426 int32_t DpShellCommand::HandleSubscribeOption(int option, std::string& deviceId,
427     std::list<std::string>& serviceIds)
428 {
429     HILOGI("%{public}s start, option: %{public}d", __func__, option);
430     int32_t result = ERR_OK;
431     switch (option) {
432         case 'h': {
433             HILOGI("'dp subscribe' %{public}s", argv_[optind - 1]);
434             result = OHOS::ERR_INVALID_VALUE;
435             break;
436         }
437         case 's': {
438             if (optarg == nullptr) {
439                 resultReceiver_.append("error: option, requires a value\n");
440                 result = OHOS::ERR_INVALID_VALUE;
441                 break;
442             }
443             std::stringstream input(optarg);
444             std::string temp;
445             while (std::getline(input, temp, ' ')) {
446                 serviceIds.push_back(temp);
447             }
448             break;
449         }
450         case 'd': {
451             if (optarg == nullptr) {
452                 resultReceiver_.append("error: option, requires a value\n");
453                 result = OHOS::ERR_INVALID_VALUE;
454                 break;
455             }
456             deviceId = optarg;
457             break;
458         }
459         default: {
460             result = OHOS::ERR_INVALID_VALUE;
461             HILOGE("'dp subscribe' invalid option.");
462             break;
463         }
464     }
465     return result;
466 }
467 
OnSyncCompleted(const SyncResult & syncResults)468 void ProfileEventCallback::OnSyncCompleted(const SyncResult& syncResults)
469 {
470     HILOGI("OnSyncCompleted");
471 }
472 
OnProfileChanged(const ProfileChangeNotification & changeNotification)473 void ProfileEventCallback::OnProfileChanged(const ProfileChangeNotification& changeNotification)
474 {
475     HILOGI("OnProfileChanged");
476 }
477 }  // namespace DeviceProfile
478 }  // namespace OHOS