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