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