1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi.p2p;
18 
19 import android.content.Context;
20 import android.net.MacAddress;
21 import android.net.NetworkInfo;
22 import android.net.wifi.SynchronousExecutor;
23 import android.net.wifi.p2p.WifiP2pConfig;
24 import android.net.wifi.p2p.WifiP2pDevice;
25 import android.net.wifi.p2p.WifiP2pDeviceList;
26 import android.net.wifi.p2p.WifiP2pGroup;
27 import android.net.wifi.p2p.WifiP2pGroupList;
28 import android.net.wifi.p2p.WifiP2pInfo;
29 import android.net.wifi.p2p.WifiP2pManager;
30 import android.os.Binder;
31 import android.os.Message;
32 import android.os.Process;
33 
34 import com.android.internal.util.Protocol;
35 import com.android.modules.utils.BasicShellCommandHandler;
36 import com.android.modules.utils.build.SdkLevel;
37 
38 import java.io.PrintWriter;
39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit;
41 
42 /**
43  * Interprets and executes 'adb shell cmd wifip2p [args]'.
44  * The leading command name is defined by android.content.Context.WIFI_P2P_SERVICE.
45  */
46 public class WifiP2pShellCommand extends BasicShellCommandHandler {
47     private static final String TAG = "WifiP2pShellCommand";
48 
49     private static WifiP2pManager.Channel sWifiP2pChannel;
50 
51     private final Context mContext;
52 
53     private final WifiP2pManager mWifiP2pManager;
54 
WifiP2pShellCommand(Context context)55     public WifiP2pShellCommand(Context context) {
56         mContext = context;
57         mWifiP2pManager = mContext.getSystemService(WifiP2pManager.class);
58     }
59 
handleCommand(String cmd, PrintWriter pw)60     private int handleCommand(String cmd, PrintWriter pw) throws Exception {
61         CountDownLatch countDownLatch = new CountDownLatch(1);
62         WifiP2pManager.ActionListener actionListener = new WifiP2pManager.ActionListener() {
63             @Override
64             public void onSuccess() {
65                 countDownLatch.countDown();
66             }
67             @Override
68             public void onFailure(int reason) {
69                 pw.println("FAILED with reason " + reason);
70                 countDownLatch.countDown();
71             }
72         };
73 
74         switch (cmd) {
75             case "init":
76                 if (null != sWifiP2pChannel) sWifiP2pChannel.close();
77                 sWifiP2pChannel = mWifiP2pManager.initialize(
78                         mContext, mContext.getMainLooper(), null);
79                 if (null == sWifiP2pChannel) {
80                     pw.println("Cannot initialize p2p channel.");
81                     return -1;
82                 }
83                 return 0;
84             case "deinit":
85                 if (null != sWifiP2pChannel) sWifiP2pChannel.close();
86                 sWifiP2pChannel = null;
87                 return 0;
88             case "start-peer-discovery":
89                 mWifiP2pManager.discoverPeers(sWifiP2pChannel, actionListener);
90                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
91                 return 0;
92             case "start-peer-discovery-on-social-channels":
93                 if (!SdkLevel.isAtLeastT()) {
94                     pw.println("This feature is only supported on SdkLevel T or later.");
95                     return -1;
96                 }
97                 mWifiP2pManager.discoverPeersOnSocialChannels(sWifiP2pChannel, actionListener);
98                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
99                 return 0;
100             case "start-peer-discovery-on-specific-frequency":
101                 if (!SdkLevel.isAtLeastT()) {
102                     pw.println("This feature is only supported on SdkLevel T or later.");
103                     return -1;
104                 }
105                 int frequencyMhz;
106                 try {
107                     frequencyMhz = Integer.parseInt(getNextArgRequired());
108                 } catch (NumberFormatException e) {
109                     pw.println(
110                             "Invalid argument to 'start-peer-discovery-on-specific-frequency' "
111                                     + "- must be an integer");
112                     return -1;
113                 }
114                 if (frequencyMhz <= 0) {
115                     pw.println("Invalid argument to 'start-peer-discovery-on-specific-frequency' "
116                             + "- must be a positive integer.");
117                     return -1;
118                 }
119                 mWifiP2pManager.discoverPeersOnSpecificFrequency(
120                         sWifiP2pChannel, frequencyMhz, actionListener);
121                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
122                 return 0;
123             case "stop-peer-discovery":
124                 mWifiP2pManager.stopPeerDiscovery(sWifiP2pChannel, actionListener);
125                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
126                 return 0;
127             case "start-service-discovery":
128                 mWifiP2pManager.discoverServices(sWifiP2pChannel, actionListener);
129                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
130                 return 0;
131             case "stop-service-discovery":
132                 mWifiP2pManager.stopPeerDiscovery(sWifiP2pChannel, actionListener);
133                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
134                 return 0;
135             case "list-peers":
136                 mWifiP2pManager.requestPeers(sWifiP2pChannel,
137                         new WifiP2pManager.PeerListListener() {
138                             @Override
139                             public void onPeersAvailable(WifiP2pDeviceList peers) {
140                                 pw.println(String.format("%-32s %-24s %-10s %-10s %-10s",
141                                         "Name", "Address", "DevCaps", "GroupCaps", "Status"));
142                                 for (WifiP2pDevice d: peers.getDeviceList()) {
143                                     pw.println(String.format("%-32s %-24s 0x%010x 0x%010x %-10s",
144                                             d.deviceName, d.deviceAddress,
145                                             d.deviceCapability, d.groupCapability,
146                                             wifiP2pDeviceStatusToStr(d.status)));
147                                 }
148                                 countDownLatch.countDown();
149                             }
150                         });
151                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
152                 return 0;
153             case "remove-client":
154                 if (!SdkLevel.isAtLeastT()) {
155                     pw.println("This feature is only supported on SdkLevel T or later.");
156                     return -1;
157                 }
158                 MacAddress peerAddress;
159                 try {
160                     peerAddress = MacAddress.fromString(getNextArgRequired());
161                 } catch (IllegalArgumentException e) {
162                     pw.println(
163                             "Invalid argument to 'remove-client' "
164                                     + "- must be a valid mac address");
165                     return -1;
166                 }
167                 mWifiP2pManager.removeClient(sWifiP2pChannel, peerAddress, actionListener);
168                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
169                 return 0;
170             case "cancel-connect":
171                 mWifiP2pManager.cancelConnect(sWifiP2pChannel, actionListener);
172                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
173                 return 0;
174             case "create-group":
175                 mWifiP2pManager.createGroup(sWifiP2pChannel, actionListener);
176                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
177                 return 0;
178             case "remove-group":
179                 mWifiP2pManager.removeGroup(sWifiP2pChannel, actionListener);
180                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
181                 return 0;
182             case "set-device-name":
183                 String deviceName = getNextArgRequired();
184                 mWifiP2pManager.setDeviceName(sWifiP2pChannel, deviceName, actionListener);
185                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
186                 return 0;
187             case "get-connection-info":
188                 mWifiP2pManager.requestConnectionInfo(sWifiP2pChannel,
189                         new WifiP2pManager.ConnectionInfoListener() {
190                             @Override
191                             public void onConnectionInfoAvailable(WifiP2pInfo info) {
192                                 pw.println(info.toString());
193                                 countDownLatch.countDown();
194                             }
195                         });
196                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
197                 return 0;
198             case "get-group-info":
199                 mWifiP2pManager.requestGroupInfo(sWifiP2pChannel,
200                         new WifiP2pManager.GroupInfoListener() {
201                             @Override
202                             public void onGroupInfoAvailable(WifiP2pGroup group) {
203                                 pw.println(group);
204                                 countDownLatch.countDown();
205                             }
206                         });
207                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
208                 return 0;
209             case "get-state":
210                 mWifiP2pManager.requestP2pState(sWifiP2pChannel,
211                         new WifiP2pManager.P2pStateListener() {
212                             @Override
213                             public void onP2pStateAvailable(int state) {
214                                 switch (state) {
215                                     case WifiP2pManager.WIFI_P2P_STATE_DISABLED:
216                                         pw.println("DISABLED");
217                                         break;
218                                     case WifiP2pManager.WIFI_P2P_STATE_ENABLED:
219                                         pw.println("ENABLED");
220                                         break;
221                                     default:
222                                         pw.println("UNKNOWN");
223                                         break;
224                                 }
225                                 countDownLatch.countDown();
226                             }
227                         });
228                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
229                 return 0;
230             case "get-discovery-state":
231                 mWifiP2pManager.requestDiscoveryState(sWifiP2pChannel,
232                         new WifiP2pManager.DiscoveryStateListener() {
233                             @Override
234                             public void onDiscoveryStateAvailable(int state) {
235                                 switch (state) {
236                                     case WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED:
237                                         pw.println("STARTED");
238                                         break;
239                                     case WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED:
240                                         pw.println("STOPPED");
241                                         break;
242                                     default:
243                                         pw.println("UNKNOWN");
244                                         break;
245                                 }
246                                 countDownLatch.countDown();
247                             }
248                         });
249                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
250                 return 0;
251             case "get-listen-state": {
252                 mWifiP2pManager.getListenState(sWifiP2pChannel, new SynchronousExecutor(),
253                         state -> {
254                             switch (state) {
255                                 case WifiP2pManager.WIFI_P2P_LISTEN_STARTED:
256                                     pw.println("STARTED");
257                                     break;
258                                 case WifiP2pManager.WIFI_P2P_LISTEN_STOPPED:
259                                     pw.println("STOPPED");
260                                     break;
261                                 default:
262                                     pw.println("UNKNOWN");
263                                     break;
264                             }
265                             countDownLatch.countDown();
266                         });
267                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
268                 return 0;
269             }
270             case "get-network-info":
271                 mWifiP2pManager.requestNetworkInfo(sWifiP2pChannel,
272                         new WifiP2pManager.NetworkInfoListener() {
273                             @Override
274                             public void onNetworkInfoAvailable(NetworkInfo networkInfo) {
275                                 pw.println(networkInfo.toString());
276                                 countDownLatch.countDown();
277                             }
278                         });
279                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
280                 return 0;
281             case "get-device-info":
282                 mWifiP2pManager.requestDeviceInfo(sWifiP2pChannel,
283                         new WifiP2pManager.DeviceInfoListener() {
284                             @Override
285                             public void onDeviceInfoAvailable(WifiP2pDevice dev) {
286                                 pw.println(dev.toString());
287                                 countDownLatch.countDown();
288                             }
289                         });
290                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
291                 return 0;
292             case "list-saved-groups":
293                 mWifiP2pManager.requestPersistentGroupInfo(sWifiP2pChannel,
294                         new WifiP2pManager.PersistentGroupInfoListener() {
295                             @Override
296                             public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups) {
297                                 pw.println(groups.toString());
298                                 countDownLatch.countDown();
299                             }
300                         });
301                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
302                 return 0;
303             case "delete-saved-group":
304                 int netId;
305                 try {
306                     netId = Integer.parseInt(getNextArgRequired());
307                 } catch (NumberFormatException e) {
308                     pw.println(
309                             "Invalid argument to 'delete-saved-group' "
310                                     + "- must be an integer");
311                     return -1;
312                 }
313                 if (netId < 0) {
314                     pw.println("Invalid argument to 'delete-saved-group' "
315                             + "- must be 0 or a positive integer.");
316                     return -1;
317                 }
318                 mWifiP2pManager.deletePersistentGroup(sWifiP2pChannel, netId, actionListener);
319                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
320                 return 0;
321             case "set-channels":
322                 int listeningChannel, operatingChannel;
323                 try {
324                     listeningChannel = Integer.parseInt(getNextArgRequired());
325                     operatingChannel = Integer.parseInt(getNextArgRequired());
326                 } catch (NumberFormatException e) {
327                     pw.println(
328                             "Invalid argument to 'set-channels' "
329                                     + "- must be an integer");
330                     return -1;
331                 }
332                 if (listeningChannel < 0 || operatingChannel < 0) {
333                     pw.println("Invalid argument to 'set-channels' "
334                             + "- must be 0 or a positive integer.");
335                     return -1;
336                 }
337                 mWifiP2pManager.setWifiP2pChannels(sWifiP2pChannel,
338                         listeningChannel, operatingChannel, actionListener);
339                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
340                 return 0;
341             case "start-listening":
342                 mWifiP2pManager.startListening(sWifiP2pChannel, actionListener);
343                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
344                 return 0;
345             case "stop-listening":
346                 mWifiP2pManager.stopListening(sWifiP2pChannel, actionListener);
347                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
348                 return 0;
349             case "set-miracast-mode":
350                 int mode;
351                 try {
352                     mode = Integer.parseInt(getNextArgRequired());
353                 } catch (NumberFormatException e) {
354                     pw.println("Invalid argument to 'set-miracast-mode' "
355                             + "- must be an integer");
356                     return -1;
357                 }
358                 if (mode < 0) {
359                     pw.println("Invalid argument to 'set-miracast-mode' "
360                             + "- must be 0 or a positive integer.");
361                     return -1;
362                 }
363                 mWifiP2pManager.setMiracastMode(mode);
364                 return 0;
365             case "factory-reset":
366                 mWifiP2pManager.factoryReset(sWifiP2pChannel, actionListener);
367                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
368                 return 0;
369             case "connect": {
370                 WifiP2pConfig config = buildWifiP2pConfig(pw);
371                 if (null == config) {
372                     pw.println("Invalid argument to 'connect'");
373                     return -1;
374                 }
375                 mWifiP2pManager.connect(sWifiP2pChannel, config, actionListener);
376                 countDownLatch.await(3000, TimeUnit.MILLISECONDS);
377                 return 0;
378             }
379             case "accept-connection":
380                 mWifiP2pManager.getP2pStateMachineMessenger()
381                         .send(Message.obtain(null, Protocol.BASE_WIFI_P2P_SERVICE + 2));
382                 return 0;
383             case "reject-connection":
384                 mWifiP2pManager.getP2pStateMachineMessenger()
385                         .send(Message.obtain(null, Protocol.BASE_WIFI_P2P_SERVICE + 3));
386                 return 0;
387             case "create-group-with-config": {
388                 WifiP2pConfig config = prepareWifiP2pConfig(pw);
389                 if (null == config) {
390                     pw.println("Invalid argument to 'create-group-with-config'");
391                     return -1;
392                 }
393                 mWifiP2pManager.createGroup(sWifiP2pChannel, config, actionListener);
394                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
395                 return 0;
396             }
397             case "connect-with-config": {
398                 WifiP2pConfig config = prepareWifiP2pConfig(pw);
399                 if (null == config) {
400                     pw.println("Invalid argument to 'connect-with-config'");
401                     return -1;
402                 }
403                 mWifiP2pManager.connect(sWifiP2pChannel, config, actionListener);
404                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
405                 return 0;
406             }
407             default:
408                 return handleDefaultCommands(cmd);
409         }
410     }
411 
412     @Override
onCommand(String cmd)413     public int onCommand(String cmd) {
414         final PrintWriter pw = getOutPrintWriter();
415         checkRootPermission();
416 
417         // Treat no command as help command.
418         if (cmd == null || cmd.equals("")) {
419             cmd = "help";
420         }
421         if (!commandDoesNotRequireP2pAlreadyInitialized(cmd, pw)) return -1;
422 
423         try {
424             return handleCommand(cmd, pw);
425         } catch (Exception e) {
426             pw.println("Exception: " + e);
427         }
428 
429         return -1;
430     }
431 
commandDoesNotRequireP2pAlreadyInitialized(String cmd, PrintWriter pw)432     private boolean commandDoesNotRequireP2pAlreadyInitialized(String cmd, PrintWriter pw) {
433         if (cmd.equals("init")) return true;
434         if (cmd.equals("deinit")) return true;
435         if (cmd.equals("help")) return true;
436 
437         if (null == mWifiP2pManager) {
438             pw.println("P2p service is not available.");
439             return false;
440         }
441 
442         if (null == sWifiP2pChannel) {
443             pw.println("P2p client is not initialized,  execute init first.");
444             return false;
445         }
446 
447         return true;
448     }
449 
wifiP2pDeviceStatusToStr(int status)450     private String wifiP2pDeviceStatusToStr(int status) {
451         switch (status) {
452             case WifiP2pDevice.CONNECTED:
453                 return "CONNECTED";
454             case WifiP2pDevice.INVITED:
455                 return "INVITED";
456             case WifiP2pDevice.FAILED:
457                 return "FAILED";
458             case WifiP2pDevice.AVAILABLE:
459                 return "AVAILABLE";
460             case WifiP2pDevice.UNAVAILABLE:
461                 return "UNAVAILABLE";
462         }
463         return "UNKNOWN";
464     }
465 
buildWifiP2pConfig(PrintWriter pw)466     private WifiP2pConfig buildWifiP2pConfig(PrintWriter pw) {
467         String deviceAddress = getNextArgRequired();
468         int goIntent = WifiP2pConfig.GROUP_OWNER_INTENT_AUTO;
469         int ipProvisioningMode = WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP;
470         String option = getNextOption();
471         while (option != null) {
472             if (option.equals("-i")) {
473                 try {
474                     goIntent = Integer.parseInt(getNextArgRequired());
475                 } catch (NumberFormatException e) {
476                     pw.println("Invalid argument for group owner intent "
477                             + "- must be an integer");
478                     return null;
479                 }
480                 if (goIntent < WifiP2pConfig.GROUP_OWNER_INTENT_MIN
481                         || goIntent > WifiP2pConfig.GROUP_OWNER_INTENT_MAX) {
482                     pw.println("Invalid argument for group owner intent "
483                             + "- must be from "
484                             + WifiP2pConfig.GROUP_OWNER_INTENT_MIN + " to "
485                             + WifiP2pConfig.GROUP_OWNER_INTENT_MAX);
486                     return null;
487                 }
488             } else if (option.equals("-m")) {
489                 if (!SdkLevel.isAtLeastT()) {
490                     pw.println("Invalid argument for group client IP provisioning mode "
491                             + "- IP provisioning mode is supported only on SdkLevel T or later");
492                     return null;
493                 }
494                 try {
495                     ipProvisioningMode = Integer.parseInt(getNextArgRequired());
496                 } catch (NumberFormatException e) {
497                     pw.println("Invalid argument for group client IP provisioning mode "
498                             + "- must be an integer");
499                     return null;
500                 }
501                 if (ipProvisioningMode != WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP
502                         && ipProvisioningMode
503                         != WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL) {
504                     pw.println("Invalid argument for group client IP provisioning mode "
505                             + "- must be "
506                             + WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP + " or "
507                             + WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL);
508                     return null;
509                 }
510             } else {
511                 pw.println("Ignoring unknown option " + option);
512             }
513             option = getNextOption();
514         }
515         WifiP2pConfig.Builder configBuilder = new WifiP2pConfig.Builder();
516         configBuilder.setDeviceAddress(MacAddress.fromString(deviceAddress));
517         if (SdkLevel.isAtLeastT()) {
518             configBuilder.setGroupClientIpProvisioningMode(ipProvisioningMode);
519         }
520         WifiP2pConfig config = configBuilder.build();
521         config.groupOwnerIntent = goIntent;
522         return config;
523     }
524 
prepareWifiP2pConfig(PrintWriter pw)525     private WifiP2pConfig prepareWifiP2pConfig(PrintWriter pw) {
526         String networkName = getNextArgRequired();
527         String passphrase = getNextArgRequired();
528         int operatingBandOrFreq;
529         try {
530             operatingBandOrFreq = Integer.parseInt(getNextArgRequired());
531         } catch (NumberFormatException e) {
532             pw.println("Invalid argument to for wifi p2p config opeartingBandOrFreq "
533                     + "- must be an integer");
534             return null;
535         }
536         if (operatingBandOrFreq < 0) {
537             pw.println("Invalid argument to for wifi p2p config opeartingBandOrFreq "
538                     + "- must be 0 or a positive integer.");
539             return null;
540         }
541         boolean isPersistent = getNextArgRequiredTrueOrFalse("true", "false");
542         WifiP2pConfig.Builder builder = new WifiP2pConfig.Builder()
543                 .setNetworkName(networkName)
544                 .setPassphrase(passphrase);
545         if (operatingBandOrFreq < 1000) {
546             builder.setGroupOperatingBand(operatingBandOrFreq);
547         } else {
548             builder.setGroupOperatingFrequency(operatingBandOrFreq);
549         }
550         builder.enablePersistentMode(isPersistent);
551         return builder.build();
552     }
553 
getNextArgRequiredTrueOrFalse(String trueString, String falseString)554     private boolean getNextArgRequiredTrueOrFalse(String trueString, String falseString)
555             throws IllegalArgumentException {
556         String nextArg = getNextArgRequired();
557         if (trueString.equals(nextArg)) {
558             return true;
559         } else if (falseString.equals(nextArg)) {
560             return false;
561         } else {
562             throw new IllegalArgumentException("Expected '" + trueString + "' or '" + falseString
563                     + "' as next arg but got '" + nextArg + "'");
564         }
565     }
566 
checkRootPermission()567     private void checkRootPermission() {
568         final int uid = Binder.getCallingUid();
569         if (uid == Process.ROOT_UID) {
570             // Root can do anything.
571             return;
572         }
573         throw new SecurityException("Uid " + uid + " does not have access to wifip2p commands");
574     }
575 
576     @Override
onHelp()577     public void onHelp() {
578         final PrintWriter pw = getOutPrintWriter();
579 
580         pw.println("Wi-Fi P2P (wifip2p) commands:");
581         pw.println("  help");
582         pw.println("    Print this help text.");
583         pw.println("  init");
584         pw.println("    Init p2p client, this must be called before executing p2p commands.");
585         pw.println("  deinit");
586         pw.println("    De-init p2p client, this must be called at the end, or wifi service will"
587                         + " keep the p2p client and block SoftAp or NAN.");
588         pw.println("  start-peer-discovery");
589         pw.println("    Start p2p peer discovery.");
590         pw.println("  start-peer-discovery-on-social-channels");
591         pw.println("    Start p2p peer discovery on social channels.");
592         pw.println("  start-peer-discovery-on-specific-frequency <frequency>");
593         pw.println("    Start p2p peer discovery on specific frequency.");
594         pw.println("  stop-peer-discovery");
595         pw.println("    Stop p2p peer discovery.");
596         pw.println("  start-service-discovery");
597         pw.println("    Start p2p service discovery.");
598         pw.println("  stop-service-discovery");
599         pw.println("    Stop p2p service discovery.");
600         pw.println("  start-listening");
601         pw.println("    Start p2p listening.");
602         pw.println("  stop-listening");
603         pw.println("    Stop p2p listening.");
604         pw.println("  list-peers");
605         pw.println("    List scanned peers.");
606         pw.println("  set-device-name <name>");
607         pw.println("    Set the p2p device name.");
608         pw.println("  get-connection-info");
609         pw.println("    Get current connection information.");
610         pw.println("  get-group-info");
611         pw.println("    Get current group information.");
612         pw.println("  get-network-info");
613         pw.println("    Get current P2P network information.");
614         pw.println("  get-device-info");
615         pw.println("    Get the device information");
616         pw.println("  get-state");
617         pw.println("    Get P2P state.");
618         pw.println("  get-discovery-state");
619         pw.println("    Indicate whether p2p discovery is running or not.");
620         pw.println("  get-listen-state");
621         pw.println("    Indicate whether p2p listen is running or not.");
622         pw.println("  list-saved-groups");
623         pw.println("    List saved groups.");
624         pw.println("  delete-saved-group <networkId>");
625         pw.println("    Delete a saved group.");
626         pw.println("  set-channels <listening channel> <operating channel>");
627         pw.println("    Set listening channel and operating channel.");
628         pw.println("  set-miracast-mode (0|1|2)");
629         pw.println("    Set Miracast mode. 0 is DISABLED, 1 is SOURCE, and 2 is SINK.");
630         pw.println("  factory-reset");
631         pw.println("    Do P2P factory reset.");
632         pw.println("  accept-connection");
633         pw.println("    Accept an incoming connection request.");
634         pw.println("  reject-connection");
635         pw.println("    Reject an incoming connection request.");
636         pw.println("  connect <device address> [-i <groupOwnerIntent>] "
637                         + "[-m <groupClientIpProvisioningMode>]");
638         pw.println("    <device address> - the peer's MAC address.");
639         pw.println("    <groupOwnerIntent> - Set group owner intent value. The value range is "
640                 + WifiP2pConfig.GROUP_OWNER_INTENT_MIN + " to "
641                 + WifiP2pConfig.GROUP_OWNER_INTENT_MAX);
642         pw.println("    <groupClientIpProvisioningMode> - Set group client IP provisioning "
643                         + "mode (supported on SdkLevel T or later)");
644         pw.println("        - Use '" + WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP
645                 + "' to select IPv4 DHCP which is system default behavior");
646         pw.println("        - Use '"
647                 + WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL
648                 + "' to select IPv6 link-local.");
649         pw.println("    Connect to a device with provided params.");
650         pw.println("  connect-with-config <network name> <passphrase>"
651                         + " <bandOrFreq> <persistent>");
652         pw.println("    <bandOrFreq> - select the preferred band or frequency.");
653         pw.println("        - Use '2' to select 2.4GHz band as the preferred band");
654         pw.println("        - Use '5' to select 5GHz band as the preferred band");
655         pw.println("        - Use a frequency in MHz to indicate the preferred frequency.");
656         pw.println("    <persistent> true for a persistent group; otherwise false.");
657         pw.println("    Connect to a device with a configuration.");
658         pw.println("  remove-client <peerAddress>");
659         pw.println("    <peerAddress> the MAC address of the p2p client.");
660         pw.println("    Remove the p2p client.");
661         pw.println("  cancel-connect");
662         pw.println("    Cancel an onging connection request.");
663         pw.println("  create-group");
664         pw.println("    Create a persistent autonomous group.");
665         pw.println("  create-group-with-config <network name> <passphrase>"
666                         + " <bandOrFreq> <persistent>");
667         pw.println("    <bandOrFreq> - select the preferred band or frequency.");
668         pw.println("        - Use '2' to select 2.4GHz band as the preferred band");
669         pw.println("        - Use '5' to select 5GHz band as the preferred band");
670         pw.println("        - Use a frequency in MHz to indicate the preferred frequency.");
671         pw.println("    <persistent> true for a persistent group; otherwise false.");
672         pw.println("    Create an autonomous group with a configuration.");
673         pw.println("  remove-group");
674         pw.println("    Remove current formed group.");
675         pw.println();
676     }
677 }
678