• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
22 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
23 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
24 import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED;
25 import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_DISCONNECT;
26 import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_LINGER;
27 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
28 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
29 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
30 
31 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP;
32 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE;
33 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_NAN;
34 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_P2P;
35 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA;
36 import static com.android.server.wifi.SelfRecovery.REASON_API_CALL;
37 
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.hardware.display.DisplayManager;
43 import android.net.ConnectivityManager;
44 import android.net.MacAddress;
45 import android.net.Network;
46 import android.net.NetworkCapabilities;
47 import android.net.NetworkRequest;
48 import android.net.wifi.IActionListener;
49 import android.net.wifi.IDppCallback;
50 import android.net.wifi.ILocalOnlyHotspotCallback;
51 import android.net.wifi.IPnoScanResultsCallback;
52 import android.net.wifi.IScoreUpdateObserver;
53 import android.net.wifi.ISoftApCallback;
54 import android.net.wifi.IWifiConnectedNetworkScorer;
55 import android.net.wifi.ScanResult;
56 import android.net.wifi.SoftApCapability;
57 import android.net.wifi.SoftApConfiguration;
58 import android.net.wifi.SoftApInfo;
59 import android.net.wifi.SupplicantState;
60 import android.net.wifi.WifiClient;
61 import android.net.wifi.WifiConfiguration;
62 import android.net.wifi.WifiConnectedSessionInfo;
63 import android.net.wifi.WifiContext;
64 import android.net.wifi.WifiInfo;
65 import android.net.wifi.WifiManager;
66 import android.net.wifi.WifiNetworkSpecifier;
67 import android.net.wifi.WifiNetworkSuggestion;
68 import android.net.wifi.WifiScanner;
69 import android.net.wifi.WifiSsid;
70 import android.net.wifi.util.ScanResultUtil;
71 import android.os.Binder;
72 import android.os.Build;
73 import android.os.Bundle;
74 import android.os.PatternMatcher;
75 import android.os.Process;
76 import android.os.RemoteException;
77 import android.os.SystemClock;
78 import android.os.WorkSource;
79 import android.telephony.Annotation;
80 import android.telephony.PhysicalChannelConfig;
81 import android.telephony.SubscriptionManager;
82 import android.telephony.TelephonyManager;
83 import android.text.TextUtils;
84 import android.util.Log;
85 import android.util.Pair;
86 import android.util.SparseArray;
87 import android.view.Display;
88 
89 import androidx.annotation.NonNull;
90 import androidx.annotation.Nullable;
91 import androidx.annotation.RequiresApi;
92 
93 import com.android.internal.annotations.VisibleForTesting;
94 import com.android.modules.utils.BasicShellCommandHandler;
95 import com.android.modules.utils.ParceledListSlice;
96 import com.android.modules.utils.build.SdkLevel;
97 import com.android.server.wifi.ClientMode.LinkProbeCallback;
98 import com.android.server.wifi.coex.CoexManager;
99 import com.android.server.wifi.coex.CoexUtils;
100 import com.android.server.wifi.hotspot2.NetworkDetail;
101 import com.android.server.wifi.util.ApConfigUtil;
102 import com.android.server.wifi.util.ArrayUtils;
103 
104 import libcore.util.HexEncoding;
105 
106 import java.io.PrintWriter;
107 import java.nio.charset.StandardCharsets;
108 import java.util.ArrayList;
109 import java.util.Arrays;
110 import java.util.Collection;
111 import java.util.Collections;
112 import java.util.Comparator;
113 import java.util.List;
114 import java.util.Map;
115 import java.util.Set;
116 import java.util.concurrent.ArrayBlockingQueue;
117 import java.util.concurrent.ConcurrentHashMap;
118 import java.util.concurrent.CountDownLatch;
119 import java.util.concurrent.TimeUnit;
120 import java.util.stream.Collectors;
121 
122 /**
123  * Interprets and executes 'adb shell cmd wifi [args]'.
124  *
125  * To add new commands:
126  * - onCommand: Add a case "<command>" execute. Return a 0
127  *   if command executed successfully.
128  * - onHelp: add a description string.
129  *
130  * Permissions: currently root permission is required for some commands. Others will
131  * enforce the corresponding API permissions.
132  */
133 public class WifiShellCommand extends BasicShellCommandHandler {
134     @VisibleForTesting
135     public static String SHELL_PACKAGE_NAME = "com.android.shell";
136 
137     // These don't require root access.
138     // However, these do perform permission checks in the corresponding WifiService methods.
139     private static final String[] NON_PRIVILEGED_COMMANDS = {
140             "add-suggestion",
141             "forget-network",
142             "get-country-code",
143             "help",
144             "-h",
145             "is-verbose-logging",
146             "list-scan-results",
147             "list-networks",
148             "list-suggestions",
149             "remove-suggestion",
150             "remove-all-suggestions",
151             "reset-connected-score",
152             "set-connected-score",
153             "set-scan-always-available",
154             "set-verbose-logging",
155             "set-wifi-enabled",
156             "set-passpoint-enabled",
157             "set-multi-internet-state",
158             "start-scan",
159             "start-softap",
160             "status",
161             "stop-softap",
162             "query-interface",
163             "interface-priority-interactive-mode",
164             "set-one-shot-screen-on-delay-ms",
165             "set-ipreach-disconnect",
166             "get-ipreach-disconnect",
167     };
168 
169     private static final Map<String, Pair<NetworkRequest, ConnectivityManager.NetworkCallback>>
170             sActiveRequests = new ConcurrentHashMap<>();
171 
172     private final ActiveModeWarden mActiveModeWarden;
173     private final WifiGlobals mWifiGlobals;
174     private final WifiLockManager mWifiLockManager;
175     private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
176     private final WifiConfigManager mWifiConfigManager;
177     private final WifiNative mWifiNative;
178     private final CoexManager mCoexManager;
179     private final WifiCountryCode mWifiCountryCode;
180     private final WifiLastResortWatchdog mWifiLastResortWatchdog;
181     private final WifiServiceImpl mWifiService;
182     private final WifiContext mContext;
183     private final ConnectivityManager mConnectivityManager;
184     private final WifiCarrierInfoManager mWifiCarrierInfoManager;
185     private final WifiNetworkFactory mWifiNetworkFactory;
186     private final SelfRecovery mSelfRecovery;
187     private final WifiThreadRunner mWifiThreadRunner;
188     private final WifiApConfigStore mWifiApConfigStore;
189     private int mSapState = WifiManager.WIFI_STATE_UNKNOWN;
190     private final ScanRequestProxy mScanRequestProxy;
191     private final @NonNull WifiDialogManager mWifiDialogManager;
192     private final HalDeviceManager mHalDeviceManager;
193     private final InterfaceConflictManager mInterfaceConflictManager;
194 
195     private class SoftApCallbackProxy extends ISoftApCallback.Stub {
196         private final PrintWriter mPrintWriter;
197         private final CountDownLatch mCountDownLatch;
198 
SoftApCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch)199         SoftApCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch) {
200             mPrintWriter = printWriter;
201             mCountDownLatch = countDownLatch;
202         }
203 
204         @Override
onStateChanged(int state, int failureReason)205         public void onStateChanged(int state, int failureReason) {
206             mPrintWriter.println("onStateChanged with state: " + state
207                     + " failure reason: " + failureReason);
208             mSapState = state;
209             if (state == WifiManager.WIFI_AP_STATE_ENABLED) {
210                 mPrintWriter.println(" SAP is enabled successfully");
211                 // Skip countDown() and wait for onInfoChanged() which has
212                 // the confirmed softAp channel information
213             } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
214                 mPrintWriter.println(" SAP is disabled");
215             } else if (state == WifiManager.WIFI_AP_STATE_FAILED) {
216                 mPrintWriter.println(" SAP failed to start");
217                 mCountDownLatch.countDown();
218             }
219         }
220 
221         @Override
onConnectedClientsOrInfoChanged(Map<String, SoftApInfo> infos, Map<String, List<WifiClient>> clients, boolean isBridged, boolean isRegistration)222         public void onConnectedClientsOrInfoChanged(Map<String, SoftApInfo> infos,
223                 Map<String, List<WifiClient>> clients, boolean isBridged,
224                 boolean isRegistration) {
225             mPrintWriter.println("onConnectedClientsOrInfoChanged, infos: " + infos
226                     + ", clients: " + clients + ", isBridged: " + isBridged);
227             if (mSapState == WifiManager.WIFI_AP_STATE_ENABLED && infos.size() != 0) {
228                 mCountDownLatch.countDown();
229             }
230         }
231 
232         @Override
onCapabilityChanged(SoftApCapability capability)233         public void onCapabilityChanged(SoftApCapability capability) {
234             mPrintWriter.println("onCapabilityChanged " + capability);
235         }
236 
237         @Override
onBlockedClientConnecting(WifiClient client, int reason)238         public void onBlockedClientConnecting(WifiClient client, int reason) {
239         }
240     }
241 
242     /**
243      * Used for shell command testing of DPP feature.
244      */
245     public static class DppCallbackProxy extends IDppCallback.Stub {
246         private final PrintWriter mPrintWriter;
247         private final CountDownLatch mCountDownLatch;
248         private static final int STATUS_SUCCESS = 0;
249         private static final int STATUS_PROGRESS = 1;
250         private static final int STATUS_FAILURE = 2;
251 
DppCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch)252         DppCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch) {
253             mPrintWriter = printWriter;
254             mCountDownLatch = countDownLatch;
255         }
256 
257         @Override
onSuccessConfigReceived(int networkId)258         public void onSuccessConfigReceived(int networkId) {
259             mPrintWriter.println("onSuccessConfigReceived. netId=" + networkId);
260             mCountDownLatch.countDown();
261         }
262 
263         @Override
onSuccess(int status)264         public void onSuccess(int status) {
265             mPrintWriter.println("onSuccess status=" + statusToString(STATUS_SUCCESS, status));
266             mCountDownLatch.countDown();
267         }
268 
269         @Override
onFailure(int status, String ssid, String channelList, int[] bandArray)270         public void onFailure(int status, String ssid, String channelList, int[] bandArray) {
271             mPrintWriter.println("onFailure. status=" + statusToString(STATUS_FAILURE, status)
272                     + "ssid=" + ssid + "channelList=" + channelList);
273             mCountDownLatch.countDown();
274         }
275 
276         @Override
onProgress(int status)277         public void onProgress(int status) {
278             mPrintWriter.println("onProgress status=" + statusToString(STATUS_PROGRESS, status));
279         }
280 
281         @Override
onBootstrapUriGenerated(String uri)282         public void onBootstrapUriGenerated(String uri) {
283             mPrintWriter.println("onBootstrapUriGenerated URI = " + uri);
284         }
285 
statusToString(int type, int status)286         private String statusToString(int type, int status) {
287             switch (type) {
288                 case STATUS_SUCCESS: {
289                     switch (status) {
290                         case 0:
291                             return "CONFIGURATION_SENT";
292                         case 1:
293                             return "CONFIGURATION_APPLIED";
294                         default:
295                             return "Unknown success code";
296                     }
297                 }
298                 case STATUS_PROGRESS: {
299                     switch (status) {
300                         case 0:
301                             return "AUTHENTICATION_SUCCESS";
302                         case 1:
303                             return "RESPONSE_PENDING";
304                         case 2:
305                             return "CONFIGURATION_SENT_WAITING_RESPONSE";
306                         case 3:
307                             return "CONFIGURATION_ACCEPTED";
308                         default:
309                             return "Unknown progress code";
310                     }
311                 }
312                 case STATUS_FAILURE: {
313                     switch (status) {
314                         case -1:
315                             return "INVALID_URI";
316                         case -2:
317                             return "AUTHENTICATION";
318                         case -3:
319                             return "NOT_COMPATIBLE";
320                         case -4:
321                             return "CONFIGURATION";
322                         case -5:
323                             return "BUSY";
324                         case -6:
325                             return "TIMEOUT";
326                         case -7:
327                             return "GENERIC";
328                         case -8:
329                             return "NOT_SUPPORTED";
330                         case -9:
331                             return "INVALID_NETWORK";
332                         case -10:
333                             return "CANNOT_FIND_NETWORK";
334                         case -11:
335                             return "ENROLLEE_AUTHENTICATION";
336                         case -12:
337                             return "ENROLLEE_REJECTED_CONFIGURATION";
338                         case -13:
339                             return "URI_GENERATION";
340                         case -14:
341                             return "ENROLLEE_FAILED_TO_SCAN_NETWORK_CHANNEL";
342                         default:
343                             return "Unknown failure code";
344                     }
345                 }
346                 default :
347                     return "Unknown status type";
348             }
349         }
350     }
351 
352     /**
353      * Used for shell command testing of scorer.
354      */
355     public static class WifiScorer extends IWifiConnectedNetworkScorer.Stub {
356         private final WifiServiceImpl mWifiService;
357         private final CountDownLatch mCountDownLatch;
358         private Integer mSessionId;
359         private IScoreUpdateObserver mScoreUpdateObserver;
360 
WifiScorer(WifiServiceImpl wifiService, CountDownLatch countDownLatch)361         public WifiScorer(WifiServiceImpl wifiService, CountDownLatch countDownLatch) {
362             mWifiService = wifiService;
363             mCountDownLatch  = countDownLatch;
364         }
365 
366         @Override
onStart(WifiConnectedSessionInfo sessionInfo)367         public void onStart(WifiConnectedSessionInfo sessionInfo) {
368             mSessionId = sessionInfo.getSessionId();
369             mCountDownLatch.countDown();
370         }
371         @Override
onStop(int sessionId)372         public void onStop(int sessionId) {
373             // clear the external scorer on disconnect.
374             mWifiService.clearWifiConnectedNetworkScorer();
375         }
376         @Override
onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl)377         public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) {
378             mScoreUpdateObserver = observerImpl;
379             mCountDownLatch.countDown();
380         }
381 
getSessionId()382         public Integer getSessionId() {
383             return mSessionId;
384         }
385 
getScoreUpdateObserver()386         public IScoreUpdateObserver getScoreUpdateObserver() {
387             return mScoreUpdateObserver;
388         }
389     }
390 
WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, WifiContext context, WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner)391     WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, WifiContext context,
392             WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner) {
393         mWifiGlobals = wifiGlobals;
394         mWifiThreadRunner = wifiThreadRunner;
395         mActiveModeWarden = wifiInjector.getActiveModeWarden();
396         mWifiLockManager = wifiInjector.getWifiLockManager();
397         mWifiNetworkSuggestionsManager = wifiInjector.getWifiNetworkSuggestionsManager();
398         mWifiConfigManager = wifiInjector.getWifiConfigManager();
399         mWifiNative = wifiInjector.getWifiNative();
400         mCoexManager = wifiInjector.getCoexManager();
401         mWifiCountryCode = wifiInjector.getWifiCountryCode();
402         mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
403         mWifiService = wifiService;
404         mContext = context;
405         mConnectivityManager = context.getSystemService(ConnectivityManager.class);
406         mWifiCarrierInfoManager = wifiInjector.getWifiCarrierInfoManager();
407         mWifiNetworkFactory = wifiInjector.getWifiNetworkFactory();
408         mSelfRecovery = wifiInjector.getSelfRecovery();
409         mWifiApConfigStore = wifiInjector.getWifiApConfigStore();
410         mScanRequestProxy = wifiInjector.getScanRequestProxy();
411         mWifiDialogManager = wifiInjector.getWifiDialogManager();
412         mHalDeviceManager = wifiInjector.getHalDeviceManager();
413         mInterfaceConflictManager = wifiInjector.getInterfaceConflictManager();
414     }
415 
416     @Override
onCommand(String cmd)417     public int onCommand(String cmd) {
418         // Treat no command as help command.
419         if (TextUtils.isEmpty(cmd)) {
420             cmd = "help";
421         }
422         // Explicit exclusion from root permission
423         if (ArrayUtils.indexOf(NON_PRIVILEGED_COMMANDS, cmd) == -1) {
424             final int uid = Binder.getCallingUid();
425             if (uid != Process.ROOT_UID) {
426                 throw new SecurityException(
427                         "Uid " + uid + " does not have access to " + cmd + " wifi command "
428                                 + "(or such command doesn't exist)");
429             }
430         }
431         final PrintWriter pw = getOutPrintWriter();
432         try {
433             switch (cmd) {
434                 case "set-ipreach-disconnect": {
435                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
436                     mWifiGlobals.setIpReachabilityDisconnectEnabled(enabled);
437                     return 0;
438                 }
439                 case "get-ipreach-disconnect":
440                     pw.println("IPREACH_DISCONNECT state is "
441                             + mWifiGlobals.getIpReachabilityDisconnectEnabled());
442                     return 0;
443                 case "set-poll-rssi-interval-msecs":
444                     int newPollIntervalMsecs;
445                     try {
446                         newPollIntervalMsecs = Integer.parseInt(getNextArgRequired());
447                     } catch (NumberFormatException e) {
448                         pw.println(
449                                 "Invalid argument to 'set-poll-rssi-interval-msecs' "
450                                         + "- must be a positive integer");
451                         return -1;
452                     }
453 
454                     if (newPollIntervalMsecs < 1) {
455                         pw.println(
456                                 "Invalid argument to 'set-poll-rssi-interval-msecs' "
457                                         + "- must be a positive integer");
458                         return -1;
459                     }
460 
461                     mWifiGlobals.setPollRssiIntervalMillis(newPollIntervalMsecs);
462                     return 0;
463                 case "get-poll-rssi-interval-msecs":
464                     pw.println("WifiGlobals.getPollRssiIntervalMillis() = "
465                             + mWifiGlobals.getPollRssiIntervalMillis());
466                     return 0;
467                 case "force-hi-perf-mode": {
468                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
469                     if (!mWifiLockManager.forceHiPerfMode(enabled)) {
470                         pw.println("Command execution failed");
471                     }
472                     return 0;
473                 }
474                 case "force-low-latency-mode": {
475                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
476                     if (!mWifiLockManager.forceLowLatencyMode(enabled)) {
477                         pw.println("Command execution failed");
478                     }
479                     return 0;
480                 }
481                 case "network-suggestions-set-user-approved": {
482                     String packageName = getNextArgRequired();
483                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
484                     mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(approved,
485                             Binder.getCallingUid(), packageName);
486                     return 0;
487                 }
488                 case "network-suggestions-has-user-approved": {
489                     String packageName = getNextArgRequired();
490                     boolean hasUserApproved =
491                             mWifiNetworkSuggestionsManager.hasUserApprovedForApp(packageName);
492                     pw.println(hasUserApproved ? "yes" : "no");
493                     return 0;
494                 }
495                 case "imsi-protection-exemption-set-user-approved-for-carrier": {
496                     String arg1 = getNextArgRequired();
497                     int carrierId = -1;
498                     try {
499                         carrierId = Integer.parseInt(arg1);
500                     } catch (NumberFormatException e) {
501                         pw.println("Invalid argument to "
502                                 + "'imsi-protection-exemption-set-user-approved-for-carrier' "
503                                 + "- carrierId must be an Integer");
504                         return -1;
505                     }
506                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
507                     mWifiCarrierInfoManager
508                             .setHasUserApprovedImsiPrivacyExemptionForCarrier(approved, carrierId);
509                     return 0;
510                 }
511                 case "imsi-protection-exemption-has-user-approved-for-carrier": {
512                     String arg1 = getNextArgRequired();
513                     int carrierId = -1;
514                     try {
515                         carrierId = Integer.parseInt(arg1);
516                     } catch (NumberFormatException e) {
517                         pw.println("Invalid argument to "
518                                 + "'imsi-protection-exemption-has-user-approved-for-carrier' "
519                                 + "- 'carrierId' must be an Integer");
520                         return -1;
521                     }
522                     boolean hasUserApproved = mWifiCarrierInfoManager
523                             .hasUserApprovedImsiPrivacyExemptionForCarrier(carrierId);
524                     pw.println(hasUserApproved ? "yes" : "no");
525                     return 0;
526                 }
527                 case "imsi-protection-exemption-clear-user-approved-for-carrier": {
528                     String arg1 = getNextArgRequired();
529                     int carrierId = -1;
530                     try {
531                         carrierId = Integer.parseInt(arg1);
532                     } catch (NumberFormatException e) {
533                         pw.println("Invalid argument to "
534                                 + "'imsi-protection-exemption-clear-user-approved-for-carrier' "
535                                 + "- 'carrierId' must be an Integer");
536                         return -1;
537                     }
538                     mWifiCarrierInfoManager.clearImsiPrivacyExemptionForCarrier(carrierId);
539                     return 0;
540                 }
541                 case "network-requests-remove-user-approved-access-points": {
542                     String packageName = getNextArgRequired();
543                     mWifiNetworkFactory.removeUserApprovedAccessPointsForApp(packageName);
544                     return 0;
545                 }
546                 case "clear-user-disabled-networks": {
547                     mWifiConfigManager.clearUserTemporarilyDisabledList();
548                     return 0;
549                 }
550                 case "send-link-probe": {
551                     return sendLinkProbe(pw);
552                 }
553                 case "force-softap-band": {
554                     boolean forceBandEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
555                     if (forceBandEnabled) {
556                         String forcedBand = getNextArgRequired();
557                         if (forcedBand.equals("2")) {
558                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
559                                     SoftApConfiguration.BAND_2GHZ, 0);
560                         } else if (forcedBand.equals("5")) {
561                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
562                                     SoftApConfiguration.BAND_5GHZ, 0);
563                         } else if (forcedBand.equals("6")) {
564                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
565                                     SoftApConfiguration.BAND_6GHZ, 0);
566                         } else {
567                             pw.println("Invalid argument to 'force-softap-band enabled' "
568                                     + "- must be a valid band integer (2|5|6)");
569                             return -1;
570                         }
571                         return 0;
572                     } else {
573                         mWifiApConfigStore.disableForceSoftApBandOrChannel();
574                         return 0;
575                     }
576 
577                 }
578                 case "force-softap-channel": {
579                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
580                     if (enabled) {
581                         int apChannelMHz;
582                         try {
583                             apChannelMHz = Integer.parseInt(getNextArgRequired());
584                         } catch (NumberFormatException e) {
585                             pw.println("Invalid argument to 'force-softap-channel enabled' "
586                                     + "- must be a positive integer");
587                             return -1;
588                         }
589                         int apChannel = ScanResult.convertFrequencyMhzToChannelIfSupported(
590                                 apChannelMHz);
591                         int band = ApConfigUtil.convertFrequencyToBand(apChannelMHz);
592                         pw.println("channel: " + apChannel + " band: " + band);
593                         if (apChannel == -1 || band == -1) {
594                             pw.println("Invalid argument to 'force-softap-channel enabled' "
595                                     + "- must be a valid WLAN channel");
596                             return -1;
597                         }
598                         boolean isTemporarilyEnablingWifiNeeded = mWifiService.getWifiEnabledState()
599                                 != WIFI_STATE_ENABLED;
600                         if (isTemporarilyEnablingWifiNeeded) {
601                             waitForWifiEnabled(true);
602                         }
603                         // Following calls will fail if wifi is not enabled
604                         boolean isValidChannel = isApChannelMHzValid(pw, apChannelMHz);
605                         if (isTemporarilyEnablingWifiNeeded) {
606                             waitForWifiEnabled(false);
607                         }
608                         if (!isValidChannel
609                                 || (band == SoftApConfiguration.BAND_5GHZ
610                                 && !mWifiService.is5GHzBandSupported())
611                                 || (band == SoftApConfiguration.BAND_6GHZ
612                                 && !mWifiService.is6GHzBandSupported())
613                                 || (band == SoftApConfiguration.BAND_60GHZ
614                                 && !mWifiService.is60GHzBandSupported())) {
615                             pw.println("Invalid argument to 'force-softap-channel enabled' "
616                                     + "- must be a valid WLAN channel"
617                                     + " in a band supported by the device");
618                             return -1;
619                         }
620                         mWifiApConfigStore.enableForceSoftApBandOrChannel(band, apChannel);
621                         return 0;
622                     } else {
623                         mWifiApConfigStore.disableForceSoftApBandOrChannel();
624                         return 0;
625                     }
626                 }
627                 case "set-pno-request": {
628                     if (!SdkLevel.isAtLeastT()) {
629                         pw.println("This feature is only supported on SdkLevel T or later.");
630                         return -1;
631                     }
632                     String ssid = getNextArgRequired();
633                     int frequency = -1;
634                     WifiSsid wifiSsid = WifiSsid.fromString("\"" + ssid + "\"");
635                     String option = getNextOption();
636                     if (option != null) {
637                         if (option.equals("-f")) {
638                             frequency = Integer.parseInt(getNextArgRequired());
639                         } else {
640                             pw.println("Invalid argument to 'set-pno-request' "
641                                     + "- only allowed option is '-f'");
642                             return -1;
643                         }
644                     }
645                     int[] frequencies = frequency == -1 ? new int[0] : new int[] {frequency};
646                     IPnoScanResultsCallback.Stub callback = new IPnoScanResultsCallback.Stub() {
647                         @Override
648                         public void onScanResultsAvailable(List<ScanResult> scanResults) {
649                             Log.v(TAG, "PNO scan results available:");
650                             for (ScanResult result : scanResults) {
651                                 Log.v(TAG, result.getWifiSsid().toString());
652                             }
653                         }
654                         @Override
655                         public void onRegisterSuccess() {
656                             Log.v(TAG, "PNO scan request register success");
657                         }
658 
659                         @Override
660                         public void onRegisterFailed(int reason) {
661                             Log.v(TAG, "PNO scan request register failed reason=" + reason);
662                         }
663 
664                         @Override
665                         public void onRemoved(int reason) {
666                             Log.v(TAG, "PNO scan request callback removed reason=" + reason);
667                         }
668                     };
669                     pw.println("requesting PNO scan for: " + wifiSsid);
670                     mWifiService.setExternalPnoScanRequest(new Binder(), callback,
671                             Arrays.asList(wifiSsid), frequencies, mContext.getOpPackageName(),
672                             mContext.getAttributionTag());
673                     return 0;
674                 }
675                 case "clear-pno-request": {
676                     if (!SdkLevel.isAtLeastT()) {
677                         pw.println("This feature is only supported on SdkLevel T or later.");
678                         return -1;
679                     }
680                     mWifiService.clearExternalPnoScanRequest();
681                     return 0;
682                 }
683                 case "start-lohs": {
684                     CountDownLatch countDownLatch = new CountDownLatch(2);
685                     SoftApConfiguration config = buildSoftApConfiguration(pw);
686                     ILocalOnlyHotspotCallback.Stub lohsCallback =
687                             new ILocalOnlyHotspotCallback.Stub() {
688                         @Override
689                         public void onHotspotStarted(SoftApConfiguration config) {
690                             pw.println("Lohs onStarted, config = " + config);
691                             countDownLatch.countDown();
692                         }
693 
694                         @Override
695                         public void onHotspotStopped() {
696                             pw.println("Lohs onStopped");
697                             countDownLatch.countDown();
698                         }
699 
700                         @Override
701                         public void onHotspotFailed(int reason) {
702                             pw.println("Lohs onFailed: " + reason);
703                             countDownLatch.countDown();
704                         }
705                     };
706                     SoftApCallbackProxy softApCallback =
707                             new SoftApCallbackProxy(pw, countDownLatch);
708                     Bundle extras = new Bundle();
709                     if (SdkLevel.isAtLeastS()) {
710                         extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
711                                 mContext.getAttributionSource());
712                     }
713                     mWifiService.registerLocalOnlyHotspotSoftApCallback(softApCallback, extras);
714                     if (REQUEST_REGISTERED != mWifiService.startLocalOnlyHotspot(
715                               lohsCallback, SHELL_PACKAGE_NAME, null /* featureId */,
716                               config, extras)) {
717                         pw.println("Lohs failed to start. Please check config parameters");
718                     }
719                     // Wait for lohs to start and complete callback
720                     countDownLatch.await(10000, TimeUnit.MILLISECONDS);
721                     mWifiService.unregisterLocalOnlyHotspotSoftApCallback(softApCallback, extras);
722                     return 0;
723                 }
724                 case "start-softap": {
725                     CountDownLatch countDownLatch = new CountDownLatch(1);
726                     SoftApConfiguration config = buildSoftApConfiguration(pw);
727                     SoftApCallbackProxy softApCallback =
728                             new SoftApCallbackProxy(pw, countDownLatch);
729                     mWifiService.registerSoftApCallback(softApCallback);
730                     if (!mWifiService.startTetheredHotspot(config, SHELL_PACKAGE_NAME)) {
731                         pw.println("Soft AP failed to start. Please check config parameters");
732                     }
733                     // Wait for softap to start and complete callback
734                     countDownLatch.await(10000, TimeUnit.MILLISECONDS);
735                     mWifiService.unregisterSoftApCallback(softApCallback);
736                     return 0;
737                 }
738                 case "stop-lohs": {
739                     mWifiService.stopLocalOnlyHotspot();
740                     pw.println("Lohs stopped successfully");
741                     return 0;
742                 }
743                 case "stop-softap": {
744                     if (mWifiService.stopSoftAp()) {
745                         pw.println("Soft AP stopped successfully");
746                     } else {
747                         pw.println("Soft AP failed to stop");
748                     }
749                     return 0;
750                 }
751                 case "reload-resources": {
752                     mContext.resetResourceCache();
753                     return 0;
754                 }
755                 case "force-country-code": {
756                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
757                     if (enabled) {
758                         String countryCode = getNextArgRequired();
759                         if (!WifiCountryCode.isValid(countryCode)) {
760                             pw.println("Invalid argument: Country code must be a 2-Character"
761                                     + " alphanumeric code. But got countryCode " + countryCode
762                                     + " instead");
763                             return -1;
764                         }
765                         mWifiCountryCode.setOverrideCountryCode(countryCode);
766                         return 0;
767                     } else {
768                         mWifiCountryCode.clearOverrideCountryCode();
769                         return 0;
770                     }
771                 }
772                 case "get-country-code": {
773                     pw.println("Wifi Country Code = "
774                             + mWifiCountryCode.getCountryCode());
775                     return 0;
776                 }
777                 case "set-wifi-watchdog": {
778                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
779                     mWifiLastResortWatchdog.setWifiWatchdogFeature(enabled);
780                     return 0;
781                 }
782                 case "get-wifi-watchdog": {
783                     pw.println("wifi watchdog state is "
784                             + mWifiLastResortWatchdog.getWifiWatchdogFeature());
785                     return 0;
786                 }
787                 case "set-wifi-enabled": {
788                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
789                     mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled);
790                     return 0;
791                 }
792                 case "set-passpoint-enabled": {
793                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
794                     mWifiService.setWifiPasspointEnabled(enabled);
795                     return 0;
796                 }
797                 case "set-multi-internet-mode": {
798                     int mode = Integer.parseInt(getNextArgRequired());
799                     mWifiService.setStaConcurrencyForMultiInternetMode(mode);
800                     return 0;
801                 }
802                 case "set-scan-always-available": {
803                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
804                     mWifiService.setScanAlwaysAvailable(enabled, SHELL_PACKAGE_NAME);
805                     return 0;
806                 }
807                 case "get-softap-supported-features":
808                     // This command is used for vts to check softap supported features.
809                     if (ApConfigUtil.isAcsSupported(mContext)) {
810                         pw.println("wifi_softap_acs_supported");
811                     }
812                     if (ApConfigUtil.isWpa3SaeSupported(mContext)) {
813                         pw.println("wifi_softap_wpa3_sae_supported");
814                     }
815                     if ((mWifiService.getSupportedFeatures()
816                             & WifiManager.WIFI_FEATURE_BRIDGED_AP)
817                             == WifiManager.WIFI_FEATURE_BRIDGED_AP) {
818                         pw.println("wifi_softap_bridged_ap_supported");
819                     }
820                     if ((mWifiService.getSupportedFeatures()
821                             & WifiManager.WIFI_FEATURE_STA_BRIDGED_AP)
822                             == WifiManager.WIFI_FEATURE_STA_BRIDGED_AP) {
823                         pw.println("wifi_softap_bridged_ap_with_sta_supported");
824                     }
825                     return 0;
826                 case "settings-reset":
827                     mWifiNative.stopFakingScanDetails();
828                     mWifiNative.resetFakeScanDetails();
829                     mWifiService.factoryReset(SHELL_PACKAGE_NAME);
830                     return 0;
831                 case "list-scan-results":
832                     List<ScanResult> scanResults =
833                             mWifiService.getScanResults(SHELL_PACKAGE_NAME, null);
834                     if (scanResults.isEmpty()) {
835                         pw.println("No scan results");
836                     } else {
837                         ScanResultUtil.dumpScanResults(pw, scanResults,
838                                 SystemClock.elapsedRealtime());
839                     }
840                     return 0;
841                 case "start-scan":
842                     mWifiService.startScan(SHELL_PACKAGE_NAME, null);
843                     return 0;
844                 case "list-networks":
845                     ParceledListSlice<WifiConfiguration> networks =
846                             mWifiService.getConfiguredNetworks(SHELL_PACKAGE_NAME, null, false);
847                     if (networks == null || networks.getList().isEmpty()) {
848                         pw.println("No networks");
849                     } else {
850                         pw.println("Network Id      SSID                         Security type");
851                         for (WifiConfiguration network : networks.getList()) {
852                             String securityType = network.getSecurityParamsList().stream()
853                                     .map(p -> WifiConfiguration.getSecurityTypeName(
854                                                     p.getSecurityType())
855                                             + (p.isAddedByAutoUpgrade() ? "^" : ""))
856                                     .collect(Collectors.joining("/"));
857                             pw.println(String.format("%-12d %-32s %-4s",
858                                     network.networkId, WifiInfo.sanitizeSsid(network.SSID),
859                                     securityType));
860                         }
861                     }
862                     return 0;
863                 case "connect-network": {
864                     CountDownLatch countDownLatch = new CountDownLatch(1);
865                     IActionListener.Stub actionListener = new IActionListener.Stub() {
866                         @Override
867                         public void onSuccess() throws RemoteException {
868                             pw.println("Connection initiated ");
869                             countDownLatch.countDown();
870                         }
871 
872                         @Override
873                         public void onFailure(int i) throws RemoteException {
874                             pw.println("Connection failed");
875                             countDownLatch.countDown();
876                         }
877                     };
878                     WifiConfiguration config = buildWifiConfiguration(pw);
879                     mWifiService.connect(config, -1, actionListener, SHELL_PACKAGE_NAME);
880                     // wait for status.
881                     countDownLatch.await(500, TimeUnit.MILLISECONDS);
882                     setAutoJoin(pw, config.SSID, config.allowAutojoin);
883                     return 0;
884                 }
885                 case "add-network": {
886                     CountDownLatch countDownLatch = new CountDownLatch(1);
887                     IActionListener.Stub actionListener = new IActionListener.Stub() {
888                         @Override
889                         public void onSuccess() throws RemoteException {
890                             pw.println("Save successful");
891                             countDownLatch.countDown();
892                         }
893 
894                         @Override
895                         public void onFailure(int i) throws RemoteException {
896                             pw.println("Save failed");
897                             countDownLatch.countDown();
898                         }
899                     };
900                     WifiConfiguration config = buildWifiConfiguration(pw);
901                     mWifiService.save(config, actionListener, SHELL_PACKAGE_NAME);
902                     // wait for status.
903                     countDownLatch.await(500, TimeUnit.MILLISECONDS);
904                     setAutoJoin(pw, config.SSID, config.allowAutojoin);
905                     return 0;
906                 }
907                 case "forget-network": {
908                     String networkId = getNextArgRequired();
909                     CountDownLatch countDownLatch = new CountDownLatch(1);
910                     IActionListener.Stub actionListener = new IActionListener.Stub() {
911                         @Override
912                         public void onSuccess() throws RemoteException {
913                             pw.println("Forget successful");
914                             countDownLatch.countDown();
915                         }
916 
917                         @Override
918                         public void onFailure(int i) throws RemoteException {
919                             pw.println("Forget failed");
920                             countDownLatch.countDown();
921                         }
922                     };
923                     mWifiService.forget(Integer.parseInt(networkId), actionListener);
924                     // wait for status.
925                     countDownLatch.await(500, TimeUnit.MILLISECONDS);
926                     return 0;
927                 }
928                 case "pmksa-flush": {
929                     String networkId = getNextArgRequired();
930                     int netId = Integer.parseInt(networkId);
931                     WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(netId);
932                     if (config == null) {
933                         pw.println("No Wifi config corresponding to networkId: " + netId);
934                         return -1;
935                     }
936                     mWifiNative.removeNetworkCachedData(netId);
937                     return 0;
938                 }
939                 case "status":
940                     printStatus(pw);
941                     return 0;
942                 case "set-verbose-logging": {
943                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
944                     mWifiService.enableVerboseLogging(enabled ? 1 : 0);
945                     return 0;
946                 }
947                 case "is-verbose-logging": {
948                     int enabled = mWifiService.getVerboseLoggingLevel();
949                     pw.println(enabled > 0 ? "enabled" : "disabled");
950                     return 0;
951                 }
952                 case "start-restricting-auto-join-to-subscription-id": {
953                     if (!SdkLevel.isAtLeastS()) {
954                         pw.println("This feature is only supported on SdkLevel S or later.");
955                         return -1;
956                     }
957                     int subId = Integer.parseInt(getNextArgRequired());
958                     mWifiService.startRestrictingAutoJoinToSubscriptionId(subId);
959                     return 0;
960                 }
961                 case "stop-restricting-auto-join-to-subscription-id": {
962                     if (!SdkLevel.isAtLeastS()) {
963                         pw.println("This feature is only supported on SdkLevel S or later.");
964                         return -1;
965                     }
966                     mWifiService.stopRestrictingAutoJoinToSubscriptionId();
967                     return 0;
968                 }
969                 case "add-suggestion": {
970                     WifiNetworkSuggestion suggestion = buildSuggestion(pw);
971                     if (suggestion  == null) {
972                         pw.println("Invalid network suggestion parameter");
973                         return -1;
974                     }
975                     int errorCode = mWifiService.addNetworkSuggestions(
976                             Arrays.asList(suggestion), SHELL_PACKAGE_NAME, null);
977                     if (errorCode != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
978                         pw.println("Add network suggestion failed with error code: " + errorCode);
979                         return -1;
980                     }
981                     // untrusted/oem-paid networks need a corresponding NetworkRequest.
982                     if (suggestion.isUntrusted()
983                             || (SdkLevel.isAtLeastS()
984                             && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
985                         NetworkRequest.Builder networkRequestBuilder =
986                                 new NetworkRequest.Builder()
987                                         .addTransportType(TRANSPORT_WIFI);
988                         if (suggestion.isUntrusted()) {
989                             networkRequestBuilder.removeCapability(NET_CAPABILITY_TRUSTED);
990                         }
991                         if (SdkLevel.isAtLeastS()) {
992                             if (suggestion.isOemPaid()) {
993                                 networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PAID);
994                             }
995                             if (suggestion.isOemPrivate()) {
996                                 networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PRIVATE);
997                             }
998                         }
999                         NetworkRequest networkRequest = networkRequestBuilder.build();
1000                         ConnectivityManager.NetworkCallback networkCallback =
1001                                 new ConnectivityManager.NetworkCallback();
1002                         pw.println("Adding request: " + networkRequest);
1003                         mConnectivityManager.requestNetwork(networkRequest, networkCallback);
1004                         sActiveRequests.put(
1005                                 suggestion.getSsid(), Pair.create(networkRequest, networkCallback));
1006                     }
1007                     return 0;
1008                 }
1009                 case "remove-suggestion": {
1010                     String ssid = getNextArgRequired();
1011                     String action = getNextArg();
1012                     int actionCode = ACTION_REMOVE_SUGGESTION_DISCONNECT;
1013                     if (action != null && action.equals("lingering")) {
1014                         actionCode = ACTION_REMOVE_SUGGESTION_LINGER;
1015                     }
1016                     List<WifiNetworkSuggestion> suggestions =
1017                             mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
1018                     WifiNetworkSuggestion suggestion = suggestions.stream()
1019                             .filter(s -> s.getSsid().equals(ssid))
1020                             .findAny()
1021                             .orElse(null);
1022                     if (suggestion == null) {
1023                         pw.println("No matching suggestion to remove");
1024                         return -1;
1025                     }
1026                     mWifiService.removeNetworkSuggestions(
1027                             Arrays.asList(suggestion), SHELL_PACKAGE_NAME, actionCode);
1028                     // untrusted/oem-paid networks need a corresponding NetworkRequest.
1029                     if (suggestion.isUntrusted()
1030                             || (SdkLevel.isAtLeastS()
1031                             && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
1032                         Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
1033                                 sActiveRequests.remove(suggestion.getSsid());
1034                         if (nrAndNc == null) {
1035                             pw.println("No matching request to remove");
1036                             return -1;
1037                         }
1038                         pw.println("Removing request: " + nrAndNc.first);
1039                         mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
1040                     }
1041                     return 0;
1042                 }
1043                 case "remove-all-suggestions":
1044                     mWifiService.removeNetworkSuggestions(
1045                             Collections.emptyList(), SHELL_PACKAGE_NAME,
1046                             WifiManager.ACTION_REMOVE_SUGGESTION_DISCONNECT);
1047                     return 0;
1048                 case "list-suggestions": {
1049                     List<WifiNetworkSuggestion> suggestions =
1050                             mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
1051                     printWifiNetworkSuggestions(pw, suggestions);
1052                     return 0;
1053                 }
1054                 case "list-all-suggestions": {
1055                     Set<WifiNetworkSuggestion> suggestions =
1056                             mWifiNetworkSuggestionsManager.getAllNetworkSuggestions();
1057                     printWifiNetworkSuggestions(pw, suggestions);
1058                     return 0;
1059                 }
1060                 case "list-suggestions-from-app": {
1061                     String packageName = getNextArgRequired();
1062                     List<WifiNetworkSuggestion> suggestions =
1063                             mWifiService.getNetworkSuggestions(packageName);
1064                     printWifiNetworkSuggestions(pw, suggestions);
1065                     return 0;
1066                 }
1067                 case "add-request": {
1068                     Pair<String, NetworkRequest> result = buildNetworkRequest(pw);
1069                     String ssid = result.first;
1070                     NetworkRequest networkRequest = result.second;
1071                     ConnectivityManager.NetworkCallback networkCallback =
1072                             new ConnectivityManager.NetworkCallback();
1073                     pw.println("Adding request: " + networkRequest);
1074                     mConnectivityManager.requestNetwork(networkRequest, networkCallback);
1075                     sActiveRequests.put(ssid, Pair.create(networkRequest, networkCallback));
1076                     return 0;
1077                 }
1078                 case "remove-request": {
1079                     String ssid = getNextArgRequired();
1080                     Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
1081                             sActiveRequests.remove(ssid);
1082                     if (nrAndNc == null) {
1083                         pw.println("No matching request to remove");
1084                         return -1;
1085                     }
1086                     pw.println("Removing request: " + nrAndNc.first);
1087                     mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
1088                     return 0;
1089                 }
1090                 case "remove-all-requests":
1091                     if (sActiveRequests.isEmpty()) {
1092                         pw.println("No active requests");
1093                         return -1;
1094                     }
1095                     for (Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc
1096                             : sActiveRequests.values()) {
1097                         pw.println("Removing request: " + nrAndNc.first);
1098                         mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
1099                     }
1100                     sActiveRequests.clear();
1101                     return 0;
1102                 case "list-requests":
1103                     if (sActiveRequests.isEmpty()) {
1104                         pw.println("No active requests");
1105                     } else {
1106                         pw.println("SSID                         NetworkRequest");
1107                         for (Map.Entry<String,
1108                                 Pair<NetworkRequest, ConnectivityManager.NetworkCallback>> entry :
1109                                 sActiveRequests.entrySet()) {
1110                             pw.println(String.format("%-32s %-4s",
1111                                     entry.getKey(), entry.getValue().first));
1112                         }
1113                     }
1114                     return 0;
1115                 case "network-requests-set-user-approved": {
1116                     String packageName = getNextArgRequired();
1117                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
1118                     mWifiNetworkFactory.setUserApprovedApp(packageName, approved);
1119                     return 0;
1120                 }
1121                 case "network-requests-has-user-approved": {
1122                     String packageName = getNextArgRequired();
1123                     boolean hasUserApproved = mWifiNetworkFactory.hasUserApprovedApp(packageName);
1124                     pw.println(hasUserApproved ? "yes" : "no");
1125                     return 0;
1126                 }
1127                 case "set-coex-cell-channels": {
1128                     if (!SdkLevel.isAtLeastS()) {
1129                         return handleDefaultCommands(cmd);
1130                     }
1131                     mCoexManager.setMockCellChannels(buildCoexCellChannels());
1132                     return 0;
1133                 }
1134                 case "reset-coex-cell-channels": {
1135                     if (!SdkLevel.isAtLeastS()) {
1136                         return handleDefaultCommands(cmd);
1137                     }
1138                     mCoexManager.resetMockCellChannels();
1139                     return 0;
1140                 }
1141                 case "get-coex-cell-channels": {
1142                     if (!SdkLevel.isAtLeastS()) {
1143                         return handleDefaultCommands(cmd);
1144                     }
1145                     pw.println("Cell channels: " + mCoexManager.getCellChannels());
1146                     return 0;
1147                 }
1148                 case "set-connected-score": {
1149                     int score = Integer.parseInt(getNextArgRequired());
1150                     CountDownLatch countDownLatch = new CountDownLatch(2);
1151                     mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer
1152                     WifiScorer connectedScorer = new WifiScorer(mWifiService, countDownLatch);
1153                     if (mWifiService.setWifiConnectedNetworkScorer(new Binder(), connectedScorer)) {
1154                         // wait for retrieving the session id & score observer.
1155                         countDownLatch.await(1000, TimeUnit.MILLISECONDS);
1156                     }
1157                     if (connectedScorer.getSessionId() == null
1158                             || connectedScorer.getScoreUpdateObserver() == null) {
1159                         pw.println("Did not receive session id and/or the score update observer. "
1160                                 + "Is the device connected to a wifi network?");
1161                         mWifiService.clearWifiConnectedNetworkScorer();
1162                         return -1;
1163                     }
1164                     pw.println("Updating score: " + score + " for session id: "
1165                             + connectedScorer.getSessionId());
1166                     try {
1167                         connectedScorer.getScoreUpdateObserver().notifyScoreUpdate(
1168                                 connectedScorer.getSessionId(), score);
1169                     } catch (RemoteException e) {
1170                         pw.println("Failed to send the score update");
1171                         mWifiService.clearWifiConnectedNetworkScorer();
1172                         return -1;
1173                     }
1174                     return 0;
1175                 }
1176                 case "reset-connected-score": {
1177                     mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer
1178                     return 0;
1179                 }
1180                 case "network-suggestions-set-as-carrier-provider": {
1181                     String packageName = getNextArgRequired();
1182                     boolean enabled = getNextArgRequiredTrueOrFalse("yes", "no");
1183                     mWifiNetworkSuggestionsManager
1184                             .setAppWorkingAsCrossCarrierProvider(packageName, enabled);
1185                     return 0;
1186                 }
1187                 case "is-network-suggestions-set-as-carrier-provider": {
1188                     String packageName = getNextArgRequired();
1189                     pw.println(mWifiNetworkSuggestionsManager
1190                             .isAppWorkingAsCrossCarrierProvider(packageName) ? "yes" : "no");
1191                     return 0;
1192                 }
1193                 case "remove-shell-app-from-suggestion_database <packageName>": {
1194                     String packageName = getNextArgRequired();
1195                     mWifiNetworkSuggestionsManager.removeApp(packageName);
1196                     return 0;
1197                 }
1198                 case "set-emergency-callback-mode": {
1199                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1200                     mActiveModeWarden.emergencyCallbackModeChanged(enabled);
1201                     return 0;
1202                 }
1203                 case "set-emergency-call-state": {
1204                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1205                     mActiveModeWarden.emergencyCallStateChanged(enabled);
1206                     return 0;
1207                 }
1208                 case "trigger-recovery": {
1209                     mSelfRecovery.trigger(REASON_API_CALL);
1210                     return 0;
1211                 }
1212                 case "add-fake-scan": {
1213                     String option = getNextOption();
1214                     boolean isHex = (option != null && option.equals("-x"));
1215                     WifiSsid wifiSsid = WifiSsid.fromBytes(isHex
1216                             ? HexEncoding.decode(getNextArgRequired())
1217                             : getNextArgRequired().getBytes(StandardCharsets.UTF_8));
1218                     String bssid = getNextArgRequired();
1219                     String capabilities = getNextArgRequired();
1220                     int frequency;
1221                     int dbm;
1222                     String freqStr = getNextArgRequired();
1223                     try {
1224                         frequency = Integer.parseInt(freqStr);
1225                     } catch (NumberFormatException e) {
1226                         pw.println(
1227                                 "Invalid frequency argument to 'add-fake-scan' "
1228                                         + "- must be an integer: " + freqStr);
1229                         return -1;
1230                     }
1231                     if (frequency <= 0) {
1232                         pw.println("Invalid frequency argument to 'add-fake-scan' - must be a "
1233                                 + "positive integer: " + freqStr);
1234                     }
1235                     String dbmString = getNextArgRequired();
1236                     try {
1237                         dbm = Integer.parseInt(dbmString);
1238                     } catch (NumberFormatException e) {
1239                         pw.println(
1240                                 "Invalid dbm argument to 'add-fake-scan' "
1241                                         + "- must be an integer: " + dbmString);
1242                         return -1;
1243                     }
1244                     ScanResult.InformationElement ieSSid = new ScanResult.InformationElement(
1245                             ScanResult.InformationElement.EID_SSID,
1246                             0,
1247                             wifiSsid.getBytes());
1248                     ScanResult.InformationElement[] ies =
1249                             new ScanResult.InformationElement[]{ieSSid};
1250                     ScanDetail sd = new ScanDetail(new NetworkDetail(bssid, ies, null, frequency),
1251                             wifiSsid, bssid, capabilities, dbm,
1252                             frequency, SystemClock.elapsedRealtime() * 1000, ies, null, null);
1253                     mWifiNative.addFakeScanDetail(sd);
1254                     return 0;
1255                 }
1256                 case "reset-fake-scans":
1257                     mWifiNative.resetFakeScanDetails();
1258                     return 0;
1259                 case "start-faking-scans":
1260                     mWifiNative.startFakingScanDetails();
1261                     mWifiService.startScan(SHELL_PACKAGE_NAME, null); // to trigger update
1262                     return 0;
1263                 case "stop-faking-scans":
1264                     mWifiNative.stopFakingScanDetails();
1265                     return 0;
1266                 case "enable-scanning": {
1267                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1268                     boolean hiddenEnabled = false;
1269                     String option = getNextOption();
1270                     if (option != null) {
1271                         if (option.equals("-h")) {
1272                             hiddenEnabled = true;
1273                         } else {
1274                             pw.println("Invalid argument to 'enable-scanning' "
1275                                     + "- only allowed option is '-h'");
1276                             return -1;
1277                         }
1278                     }
1279                     mScanRequestProxy.enableScanning(enabled, hiddenEnabled);
1280                     return 0;
1281                 }
1282                 case "launch-dialog-simple":
1283                     String title = null;
1284                     String message = null;
1285                     String messageUrl = null;
1286                     int messageUrlStart = 0;
1287                     int messageUrlEnd = 0;
1288                     String positiveButtonText = null;
1289                     String negativeButtonText = null;
1290                     String neutralButtonText = null;
1291                     String dialogOption = getNextOption();
1292                     boolean simpleTimeoutSpecified = false;
1293                     long simpleTimeoutMs = 0;
1294                     while (dialogOption != null) {
1295                         switch (dialogOption) {
1296                             case "-t":
1297                                 title = getNextArgRequired();
1298                                 break;
1299                             case "-m":
1300                                 message = getNextArgRequired();
1301                                 break;
1302                             case "-l":
1303                                 messageUrl = getNextArgRequired();
1304                                 messageUrlStart = Integer.valueOf(getNextArgRequired());
1305                                 messageUrlEnd = Integer.valueOf(getNextArgRequired());
1306                                 break;
1307                             case "-y":
1308                                 positiveButtonText = getNextArgRequired();
1309                                 break;
1310                             case "-n":
1311                                 negativeButtonText = getNextArgRequired();
1312                                 break;
1313                             case "-x":
1314                                 neutralButtonText = getNextArgRequired();
1315                                 break;
1316                             case "-c":
1317                                 simpleTimeoutMs = Integer.parseInt(getNextArgRequired());
1318                                 simpleTimeoutSpecified = true;
1319                                 break;
1320                             default:
1321                                 pw.println("Ignoring unknown option " + dialogOption);
1322                                 break;
1323                         }
1324                         dialogOption = getNextOption();
1325                     }
1326                     ArrayBlockingQueue<String> simpleQueue = new ArrayBlockingQueue<>(1);
1327                     WifiDialogManager.SimpleDialogCallback wifiEnableRequestCallback =
1328                             new WifiDialogManager.SimpleDialogCallback() {
1329                                 @Override
1330                                 public void onPositiveButtonClicked() {
1331                                     simpleQueue.offer("Positive button was clicked.");
1332                                 }
1333 
1334                                 @Override
1335                                 public void onNegativeButtonClicked() {
1336                                     simpleQueue.offer("Negative button was clicked.");
1337                                 }
1338 
1339                                 @Override
1340                                 public void onNeutralButtonClicked() {
1341                                     simpleQueue.offer("Neutral button was clicked.");
1342                                 }
1343 
1344                                 @Override
1345                                 public void onCancelled() {
1346                                     simpleQueue.offer("Dialog was cancelled.");
1347                                 }
1348                             };
1349                     WifiDialogManager.DialogHandle simpleDialogHandle =
1350                             mWifiDialogManager.createSimpleDialogWithUrl(
1351                                     title,
1352                                     message,
1353                                     messageUrl,
1354                                     messageUrlStart,
1355                                     messageUrlEnd,
1356                                     positiveButtonText,
1357                                     negativeButtonText,
1358                                     neutralButtonText,
1359                                     wifiEnableRequestCallback,
1360                                     mWifiThreadRunner);
1361                     if (simpleTimeoutSpecified) {
1362                         simpleDialogHandle.launchDialog(simpleTimeoutMs);
1363                         pw.println("Launched dialog with " + simpleTimeoutMs + " millisecond"
1364                                 + " timeout. Waiting for user response...");
1365                         pw.flush();
1366                         String dialogResponse = simpleQueue.take();
1367                         if (dialogResponse == null) {
1368                             pw.println("No response received.");
1369                         } else {
1370                             pw.println(dialogResponse);
1371                         }
1372                     } else {
1373                         simpleDialogHandle.launchDialog();
1374                         pw.println("Launched dialog. Waiting up to 15 seconds for user response"
1375                                 + " before dismissing...");
1376                         pw.flush();
1377                         String dialogResponse = simpleQueue.poll(15, TimeUnit.SECONDS);
1378                         if (dialogResponse == null) {
1379                             pw.println("No response received. Dismissing dialog.");
1380                             simpleDialogHandle.dismissDialog();
1381                         } else {
1382                             pw.println(dialogResponse);
1383                         }
1384                     }
1385                     return 0;
1386                 case "launch-dialog-p2p-invitation-sent": {
1387                     int displayId = Display.DEFAULT_DISPLAY;
1388                     String deviceName = getNextArgRequired();
1389                     String displayPin = getNextArgRequired();
1390                     String cmdOption = getNextOption();
1391                     if (cmdOption != null && cmdOption.equals("-i")) {
1392                         String displayIdStr = getNextArgRequired();
1393                         try {
1394                             displayId = Integer.parseInt(displayIdStr);
1395                         } catch (NumberFormatException e) {
1396                             pw.println("Invalid <display-id> argument to "
1397                                     + "'launch-dialog-p2p-invitation-sent' "
1398                                     + "- must be an integer: "
1399                                     + displayIdStr);
1400                             return -1;
1401                         }
1402                         DisplayManager dm = mContext.getSystemService(DisplayManager.class);
1403                         Display[] displays = dm.getDisplays();
1404                         for (Display display : displays) {
1405                             pw.println("Display: id=" + display.getDisplayId() + ", info="
1406                                     + display.getDeviceProductInfo());
1407                         }
1408                     }
1409                     mWifiDialogManager.createP2pInvitationSentDialog(deviceName, displayPin,
1410                             displayId).launchDialog();
1411                     pw.println("Launched dialog.");
1412                     return 0;
1413                 }
1414                 case "launch-dialog-p2p-invitation-received": {
1415                     String deviceName = getNextArgRequired();
1416                     boolean isPinRequested = false;
1417                     String displayPin = null;
1418                     String pinOption = getNextOption();
1419                     int displayId = Display.DEFAULT_DISPLAY;
1420                     boolean p2pInvRecTimeoutSpecified = false;
1421                     long p2pInvRecTimeout = 0;
1422                     while (pinOption != null) {
1423                         if (pinOption.equals("-p")) {
1424                             isPinRequested = true;
1425                         } else if (pinOption.equals("-d")) {
1426                             displayPin = getNextArgRequired();
1427                         } else if (pinOption.equals("-i")) {
1428                             String displayIdStr = getNextArgRequired();
1429                             try {
1430                                 displayId = Integer.parseInt(displayIdStr);
1431                             } catch (NumberFormatException e) {
1432                                 pw.println("Invalid <display-id> argument to "
1433                                         + "'launch-dialog-p2p-invitation-received' "
1434                                         + "- must be an integer: "
1435                                         + displayIdStr);
1436                                 return -1;
1437                             }
1438                             DisplayManager dm = mContext.getSystemService(DisplayManager.class);
1439                             Display[] displays = dm.getDisplays();
1440                             for (Display display : displays) {
1441                                 pw.println("Display: id=" + display.getDisplayId() + ", info="
1442                                         + display.getDeviceProductInfo());
1443                             }
1444                         } else if (pinOption.equals("-c")) {
1445                             p2pInvRecTimeout = Integer.parseInt(getNextArgRequired());
1446                             p2pInvRecTimeoutSpecified = true;
1447                         } else {
1448                             pw.println("Ignoring unknown option " + pinOption);
1449                         }
1450                         pinOption = getNextOption();
1451                     }
1452                     ArrayBlockingQueue<String> p2pInvRecQueue = new ArrayBlockingQueue<>(1);
1453                     WifiDialogManager.P2pInvitationReceivedDialogCallback callback =
1454                             new WifiDialogManager.P2pInvitationReceivedDialogCallback() {
1455                         @Override
1456                         public void onAccepted(@Nullable String optionalPin) {
1457                             p2pInvRecQueue.offer("Invitation accepted with optionalPin="
1458                                     + optionalPin);
1459                         }
1460 
1461                         @Override
1462                         public void onDeclined() {
1463                             p2pInvRecQueue.offer("Invitation declined");
1464                         }
1465                     };
1466                     WifiDialogManager.DialogHandle p2pInvitationReceivedDialogHandle =
1467                             mWifiDialogManager.createP2pInvitationReceivedDialog(
1468                                     deviceName,
1469                                     isPinRequested,
1470                                     displayPin,
1471                                     displayId,
1472                                     callback,
1473                                     mWifiThreadRunner);
1474                     if (p2pInvRecTimeoutSpecified) {
1475                         p2pInvitationReceivedDialogHandle.launchDialog(p2pInvRecTimeout);
1476                         pw.println("Launched dialog with " + p2pInvRecTimeout + " millisecond"
1477                                 + " timeout. Waiting for user response...");
1478                         pw.flush();
1479                         String dialogResponse = p2pInvRecQueue.take();
1480                         if (dialogResponse == null) {
1481                             pw.println("No response received.");
1482                         } else {
1483                             pw.println(dialogResponse);
1484                         }
1485                     } else {
1486                         p2pInvitationReceivedDialogHandle.launchDialog();
1487                         pw.println("Launched dialog. Waiting up to 15 seconds for user response"
1488                                 + " before dismissing...");
1489                         pw.flush();
1490                         String dialogResponse = p2pInvRecQueue.poll(15, TimeUnit.SECONDS);
1491                         if (dialogResponse == null) {
1492                             pw.println("No response received. Dismissing dialog.");
1493                             p2pInvitationReceivedDialogHandle.dismissDialog();
1494                         } else {
1495                             pw.println(dialogResponse);
1496                         }
1497                     }
1498                     return 0;
1499                 }
1500                 case "query-interface": {
1501                     String uidArg = getNextArgRequired();
1502                     int uid = 0;
1503                     try {
1504                         uid = Integer.parseInt(uidArg);
1505                     } catch (NumberFormatException e) {
1506                         pw.println(
1507                                 "Invalid UID specified, can't convert to an integer - " + uidArg);
1508                         return -1;
1509                     }
1510                     String packageName = getNextArgRequired();
1511 
1512                     String interfaceTypeArg = getNextArgRequired();
1513                     int interfaceType;
1514                     switch (interfaceTypeArg) {
1515                         case "STA":
1516                             interfaceType = HDM_CREATE_IFACE_STA;
1517                             break;
1518                         case "AP":
1519                             interfaceType = HDM_CREATE_IFACE_AP;
1520                             break;
1521                         case "AWARE":
1522                             interfaceType = HDM_CREATE_IFACE_NAN;
1523                             break;
1524                         case "DIRECT":
1525                             interfaceType = HDM_CREATE_IFACE_P2P;
1526                             break;
1527                         default:
1528                             pw.println("Invalid interface type - expected STA|AP|AWARE|DIRECT: "
1529                                     + interfaceTypeArg);
1530                             return -1;
1531                     }
1532                     boolean queryForNewInterface = false;
1533                     String optArg = getNextArg();
1534                     if (optArg != null) {
1535                         if (TextUtils.equals("-new", optArg)) {
1536                             queryForNewInterface = true;
1537                         } else {
1538                             pw.println("Unknown extra arg --- " + optArg);
1539                             return -1;
1540                         }
1541                     }
1542                     List<Pair<Integer, WorkSource>> details =
1543                             mHalDeviceManager.reportImpactToCreateIface(interfaceType,
1544                                     queryForNewInterface, new WorkSource(uid, packageName));
1545                     final SparseArray<String> ifaceMap = new SparseArray<String>() {{
1546                             put(HDM_CREATE_IFACE_STA, "STA");
1547                             put(HDM_CREATE_IFACE_AP, "AP");
1548                             put(HDM_CREATE_IFACE_AP_BRIDGE, "AP");
1549                             put(HDM_CREATE_IFACE_P2P, "DIRECT");
1550                             put(HDM_CREATE_IFACE_NAN, "AWARE");
1551                         }};
1552                     if (details == null) {
1553                         pw.println("Can't create interface: " + interfaceTypeArg);
1554                     } else if (details.size() == 0) {
1555                         pw.println("Interface " + interfaceTypeArg
1556                                 + " can be created without destroying any other interfaces");
1557                     } else {
1558                         pw.println("Interface " + interfaceTypeArg
1559                                 + " can be created. Following interfaces will be destroyed:");
1560                         for (Pair<Integer, WorkSource> detail : details) {
1561                             pw.println("    Type=" + ifaceMap.get(detail.first) + ", WS="
1562                                     + detail.second);
1563                         }
1564                     }
1565                     return 0;
1566                 }
1567                 case "interface-priority-interactive-mode": {
1568                     String flag = getNextArgRequired(); // enable|disable|default
1569                     switch (flag) {
1570                         case "enable":
1571                             mInterfaceConflictManager.setUserApprovalNeededOverride(true, true);
1572                             break;
1573                         case "disable":
1574                             mInterfaceConflictManager.setUserApprovalNeededOverride(true, false);
1575                             break;
1576                         case "default":
1577                             mInterfaceConflictManager.setUserApprovalNeededOverride(
1578                                     false, /* don't care */ false);
1579                             break;
1580                         default:
1581                             pw.println(
1582                                     "Invalid argument to `interface-priority-interactive-mode` - "
1583                                             + flag);
1584                             return -1;
1585                     }
1586                     return 0;
1587                 }
1588                 case "set-one-shot-screen-on-delay-ms": {
1589                     if (!SdkLevel.isAtLeastT()) {
1590                         pw.println("This feature is only supported on SdkLevel T or later.");
1591                         return -1;
1592                     }
1593                     int delay = Integer.parseInt(getNextArgRequired());
1594                     mWifiService.setOneShotScreenOnConnectivityScanDelayMillis(delay);
1595                     return 0;
1596                 }
1597                 case "start-dpp-enrollee-responder": {
1598                     CountDownLatch countDownLatch = new CountDownLatch(1);
1599                     String option = getNextOption();
1600                     String info = null;
1601                     int curve = 0;
1602                     while (option != null) {
1603                         if (option.equals("-i")) {
1604                             info = getNextArgRequired();
1605                         } else if (option.equals("-c")) {
1606                             curve = Integer.parseInt(getNextArgRequired());
1607                         } else {
1608                             pw.println("Ignoring unknown option " + option);
1609                         }
1610                         option = getNextOption();
1611                     }
1612                     mWifiService.startDppAsEnrolleeResponder(new Binder(), info, curve,
1613                             new DppCallbackProxy(pw, countDownLatch));
1614                     // Wait for DPP callback
1615                     countDownLatch.await(10000, TimeUnit.MILLISECONDS);
1616                     return 0;
1617                 }
1618                 case "start-dpp-configurator-initiator": {
1619                     CountDownLatch countDownLatch = new CountDownLatch(1);
1620                     int netId = Integer.parseInt(getNextArgRequired());
1621                     int role = Integer.parseInt(getNextArgRequired());
1622                     String enrolleeUri = getNextArgRequired();
1623                     mWifiService.startDppAsConfiguratorInitiator(new Binder(), SHELL_PACKAGE_NAME,
1624                             enrolleeUri, netId, role, new DppCallbackProxy(pw, countDownLatch));
1625                     // Wait for DPP callback
1626                     countDownLatch.await(10000, TimeUnit.MILLISECONDS);
1627                     return 0;
1628                 }
1629                 case "stop-dpp":
1630                     mWifiService.stopDppSession();
1631                     return 0;
1632                 default:
1633                     return handleDefaultCommands(cmd);
1634             }
1635         } catch (IllegalArgumentException e) {
1636             pw.println("Invalid args for " + cmd + ": " + e);
1637             return -1;
1638         } catch (Exception e) {
1639             pw.println("Exception while executing WifiShellCommand: ");
1640             e.printStackTrace(pw);
1641             return -1;
1642         }
1643     }
1644 
getNextArgRequiredTrueOrFalse(String trueString, String falseString)1645     private boolean getNextArgRequiredTrueOrFalse(String trueString, String falseString)
1646             throws IllegalArgumentException {
1647         String nextArg = getNextArgRequired();
1648         if (trueString.equals(nextArg)) {
1649             return true;
1650         } else if (falseString.equals(nextArg)) {
1651             return false;
1652         } else {
1653             throw new IllegalArgumentException("Expected '" + trueString + "' or '" + falseString
1654                     + "' as next arg but got '" + nextArg + "'");
1655         }
1656     }
1657 
buildWifiConfiguration(PrintWriter pw)1658     private WifiConfiguration buildWifiConfiguration(PrintWriter pw) {
1659         String ssid = getNextArgRequired();
1660         String type = getNextArgRequired();
1661         WifiConfiguration configuration = new WifiConfiguration();
1662         // Wrap the SSID in double quotes for UTF-8. The quotes may be removed if the SSID is in
1663         // hexadecimal digits, specified by the [-x] option below.
1664         configuration.SSID = "\"" + ssid + "\"";
1665         if (TextUtils.equals(type, "wpa3")) {
1666             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
1667             configuration.preSharedKey = "\"" + getNextArgRequired() + "\"";
1668         } else if (TextUtils.equals(type, "wpa2")) {
1669             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
1670             configuration.preSharedKey = "\"" + getNextArgRequired() + "\"";
1671         } else if (TextUtils.equals(type, "owe")) {
1672             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
1673         } else if (TextUtils.equals(type, "open")) {
1674             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
1675         } else if (TextUtils.equals(type, "dpp")) {
1676             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_DPP);
1677         } else {
1678             throw new IllegalArgumentException("Unknown network type " + type);
1679         }
1680         String option = getNextOption();
1681         while (option != null) {
1682             if (option.equals("-x")) {
1683                 configuration.SSID = ssid;
1684             } else if (option.equals("-m")) {
1685                 configuration.meteredOverride = METERED_OVERRIDE_METERED;
1686             } else if (option.equals("-d")) {
1687                 configuration.allowAutojoin = false;
1688             } else if (option.equals("-b")) {
1689                 configuration.BSSID = getNextArgRequired();
1690             } else if (option.equals("-r")) {
1691                 String macRandomizationScheme = getNextArgRequired();
1692                 if (macRandomizationScheme.equals("auto")) {
1693                     configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO;
1694                 } else if (macRandomizationScheme.equals("none")) {
1695                     configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
1696                 } else if (macRandomizationScheme.equals("persistent")) {
1697                     configuration.macRandomizationSetting =
1698                             WifiConfiguration.RANDOMIZATION_PERSISTENT;
1699                 } else if (macRandomizationScheme.equals("non_persistent")) {
1700                     if (SdkLevel.isAtLeastS()) {
1701                         configuration.macRandomizationSetting =
1702                                 WifiConfiguration.RANDOMIZATION_NON_PERSISTENT;
1703                     } else {
1704                         throw new IllegalArgumentException(
1705                                 "-r non_persistent MAC randomization not supported before S");
1706                     }
1707                 }
1708             } else if (option.equals("-h")) {
1709                 configuration.hiddenSSID = true;
1710             } else if (option.equals("-p")) {
1711                 configuration.shared = false;
1712             } else {
1713                 pw.println("Ignoring unknown option " + option);
1714             }
1715             option = getNextOption();
1716         }
1717         return configuration;
1718     }
1719 
buildSoftApConfiguration(PrintWriter pw)1720     private SoftApConfiguration buildSoftApConfiguration(PrintWriter pw) {
1721         String ssidStr = getNextArgRequired();
1722         String type = getNextArgRequired();
1723         SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
1724         configBuilder.setSsid(ssidStr);
1725         if (TextUtils.equals(type, "wpa2")) {
1726             configBuilder.setPassphrase(getNextArgRequired(),
1727                     SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
1728         } else if (TextUtils.equals(type, "wpa3")) {
1729             configBuilder.setPassphrase(getNextArgRequired(),
1730                     SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
1731         } else if (TextUtils.equals(type, "wpa3_transition")) {
1732             configBuilder.setPassphrase(getNextArgRequired(),
1733                     SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
1734         } else if (TextUtils.equals(type, "open")) {
1735             configBuilder.setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN);
1736         } else if (TextUtils.equals(type, "owe_transition")) {
1737             configBuilder.setPassphrase(null,
1738                     SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION);
1739         } else if (TextUtils.equals(type, "owe")) {
1740             configBuilder.setPassphrase(null,
1741                     SoftApConfiguration.SECURITY_TYPE_WPA3_OWE);
1742         } else {
1743             throw new IllegalArgumentException("Unknown network type " + type);
1744         }
1745         String option = getNextOption();
1746         while (option != null) {
1747             if (option.equals("-b")) {
1748                 String preferredBand = getNextArgRequired();
1749                 if (preferredBand.equals("2")) {
1750                     configBuilder.setBand(SoftApConfiguration.BAND_2GHZ);
1751                 } else if (preferredBand.equals("5")) {
1752                     configBuilder.setBand(SoftApConfiguration.BAND_5GHZ);
1753                 } else if (preferredBand.equals("6")) {
1754                     configBuilder.setBand(SoftApConfiguration.BAND_6GHZ);
1755                 } else if (preferredBand.equals("any")) {
1756                     configBuilder.setBand(SoftApConfiguration.BAND_2GHZ
1757                             | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ);
1758                 } else if (preferredBand.equals("bridged")) {
1759                     if (SdkLevel.isAtLeastS()) {
1760                         int[] dualBands = new int[] {
1761                                 SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ};
1762                         configBuilder.setBands(dualBands);
1763                     } else {
1764                         throw new IllegalArgumentException(
1765                                 "-b bridged option is not supported before S");
1766                     }
1767                 } else {
1768                     throw new IllegalArgumentException("Invalid band option " + preferredBand);
1769                 }
1770             } else if (SdkLevel.isAtLeastT() && option.equals("-x")) {
1771                 configBuilder.setWifiSsid(WifiSsid.fromString(ssidStr));
1772             } else {
1773                 pw.println("Ignoring unknown option " + option);
1774             }
1775             option = getNextOption();
1776         }
1777         return configBuilder.build();
1778     }
1779 
buildSuggestion(PrintWriter pw)1780     private WifiNetworkSuggestion buildSuggestion(PrintWriter pw) {
1781         String ssid = getNextArgRequired();
1782         String type = getNextArgRequired();
1783         WifiNetworkSuggestion.Builder suggestionBuilder =
1784                 new WifiNetworkSuggestion.Builder();
1785         suggestionBuilder.setSsid(ssid);
1786         if (TextUtils.equals(type, "wpa3")) {
1787             suggestionBuilder.setWpa3Passphrase(getNextArgRequired());
1788         } else if (TextUtils.equals(type, "wpa2")) {
1789             suggestionBuilder.setWpa2Passphrase(getNextArgRequired());
1790         } else if (TextUtils.equals(type, "owe")) {
1791             suggestionBuilder.setIsEnhancedOpen(true);
1792         } else if (TextUtils.equals(type, "open")) {
1793             // nothing to do.
1794         } else {
1795             throw new IllegalArgumentException("Unknown network type " + type);
1796         }
1797         boolean isCarrierMerged = false;
1798         String option = getNextOption();
1799         while (option != null) {
1800             if (option.equals("-u")) {
1801                 suggestionBuilder.setUntrusted(true);
1802             } else if (option.equals("-o")) {
1803                 if (SdkLevel.isAtLeastS()) {
1804                     suggestionBuilder.setOemPaid(true);
1805                 } else {
1806                     throw new IllegalArgumentException(
1807                             "-o OEM paid suggestions not supported before S");
1808                 }
1809             } else if (option.equals("-p")) {
1810                 if (SdkLevel.isAtLeastS()) {
1811                     suggestionBuilder.setOemPrivate(true);
1812                 } else {
1813                     throw new IllegalArgumentException(
1814                             "-p OEM private suggestions not supported before S");
1815                 }
1816             } else if (option.equals("-m")) {
1817                 suggestionBuilder.setIsMetered(true);
1818             } else if (option.equals("-s")) {
1819                 suggestionBuilder.setCredentialSharedWithUser(true);
1820             } else if (option.equals("-d")) {
1821                 suggestionBuilder.setIsInitialAutojoinEnabled(false);
1822             } else if (option.equals("-b")) {
1823                 suggestionBuilder.setBssid(MacAddress.fromString(getNextArgRequired()));
1824             } else if (option.equals("-r")) {
1825                 if (SdkLevel.isAtLeastS()) {
1826                     suggestionBuilder.setMacRandomizationSetting(
1827                             WifiNetworkSuggestion.RANDOMIZATION_NON_PERSISTENT);
1828                 } else {
1829                     throw new IllegalArgumentException(
1830                             "-r non_persistent MAC randomization not supported before S");
1831                 }
1832             } else if (option.equals("-a")) {
1833                 if (SdkLevel.isAtLeastS()) {
1834                     isCarrierMerged = true;
1835                 } else {
1836                     throw new IllegalArgumentException("-a option is not supported before S");
1837                 }
1838             } else if (option.equals("-i")) {
1839                 if (SdkLevel.isAtLeastS()) {
1840                     int subId = Integer.parseInt(getNextArgRequired());
1841                     suggestionBuilder.setSubscriptionId(subId);
1842                 } else {
1843                     throw new IllegalArgumentException(
1844                             "-i subscription ID option is not supported before S");
1845                 }
1846             } else if (option.equals("-c")) {
1847                 int carrierId = Integer.parseInt(getNextArgRequired());
1848                 suggestionBuilder.setCarrierId(carrierId);
1849             } else if (option.equals("-h")) {
1850                 suggestionBuilder.setIsHiddenSsid(true);
1851             } else {
1852                 pw.println("Ignoring unknown option " + option);
1853             }
1854             option = getNextOption();
1855         }
1856         WifiNetworkSuggestion suggestion = suggestionBuilder.build();
1857         if (isCarrierMerged) {
1858             if (suggestion.wifiConfiguration.subscriptionId
1859                     == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1860                 pw.println("Carrier merged network must have valid subscription Id");
1861                 return null;
1862             }
1863             suggestion.wifiConfiguration.carrierMerged = true;
1864         }
1865         return suggestion;
1866     }
1867 
buildNetworkRequest(PrintWriter pw)1868     private Pair<String, NetworkRequest> buildNetworkRequest(PrintWriter pw) {
1869         String firstOpt = getNextOption();
1870         boolean isGlob = "-g".equals(firstOpt);
1871         boolean noSsid = "-s".equals(firstOpt);
1872         String ssid = noSsid ? "NoSsid" : getNextArgRequired();
1873         String type = noSsid ? null : getNextArgRequired();
1874         WifiNetworkSpecifier.Builder specifierBuilder =
1875                 new WifiNetworkSpecifier.Builder();
1876         if (isGlob) {
1877             specifierBuilder.setSsidPattern(
1878                     new PatternMatcher(ssid, PatternMatcher.PATTERN_ADVANCED_GLOB));
1879         } else {
1880             if (ssid != null && !noSsid) specifierBuilder.setSsid(ssid);
1881         }
1882         if (type != null) {
1883             if (TextUtils.equals(type, "wpa3")) {
1884                 specifierBuilder.setWpa3Passphrase(getNextArgRequired());
1885             } else if (TextUtils.equals(type, "wpa3_transition")) {
1886                 specifierBuilder.setWpa3Passphrase(getNextArgRequired());
1887             } else if (TextUtils.equals(type, "wpa2")) {
1888                 specifierBuilder.setWpa2Passphrase(getNextArgRequired());
1889             } else if (TextUtils.equals(type, "owe")) {
1890                 specifierBuilder.setIsEnhancedOpen(true);
1891             } else if (TextUtils.equals(type, "open")) {
1892                 // nothing to do.
1893             } else {
1894                 throw new IllegalArgumentException("Unknown network type " + type);
1895             }
1896         }
1897         String bssid = null;
1898         String option = getNextOption();
1899         String ssidKey = ssid;
1900         boolean nullBssid = false;
1901         boolean hasInternet = false;
1902         while (option != null) {
1903             if (option.equals("-b")) {
1904                 bssid = getNextArgRequired();
1905             } else if (option.equals("-n")) {
1906                 nullBssid = true;
1907             } else if (option.equals("-d")) {
1908                 String band = getNextArgRequired();
1909                 ssidKey = ssidKey + "_" + band + "g";
1910                 if (band.equals("2")) {
1911                     specifierBuilder.setBand(ScanResult.WIFI_BAND_24_GHZ);
1912                 } else if (band.equals("5")) {
1913                     specifierBuilder.setBand(ScanResult.WIFI_BAND_5_GHZ);
1914                 } else if (band.equals("6")) {
1915                     specifierBuilder.setBand(ScanResult.WIFI_BAND_6_GHZ);
1916                 } else if (band.equals("60")) {
1917                     specifierBuilder.setBand(ScanResult.WIFI_BAND_60_GHZ);
1918                 } else {
1919                     throw new IllegalArgumentException("Unknown band " + band);
1920                 }
1921             } else if (option.equals("-i")) {
1922                 ssidKey = ssidKey + "_internet";
1923                 hasInternet = true;
1924             } else {
1925                 pw.println("Ignoring unknown option " + option);
1926             }
1927             option = getNextOption();
1928         }
1929         if (bssid != null && nullBssid) {
1930             throw new IllegalArgumentException("Invalid option combination: "
1931                     + "Should not use both -b and -n at the same time.");
1932         }
1933 
1934         // Permission approval bypass is only available to requests with both ssid & bssid set.
1935         // So, find scan result with the best rssi level to set in the request.
1936         if (bssid == null && !nullBssid) {
1937             ScanResult matchingScanResult =
1938                     mWifiService.getScanResults(SHELL_PACKAGE_NAME, null)
1939                             .stream()
1940                             .filter(s -> s.SSID.equals(ssid))
1941                             .max(Comparator.comparingInt(s -> s.level))
1942                             .orElse(null);
1943             if (matchingScanResult != null) {
1944                 bssid = matchingScanResult.BSSID;
1945             } else {
1946                 pw.println("No matching bssid found, request will need UI approval");
1947             }
1948         }
1949         if (bssid != null && !nullBssid) specifierBuilder.setBssid(MacAddress.fromString(bssid));
1950         NetworkRequest.Builder builder = new NetworkRequest.Builder()
1951                 .addTransportType(TRANSPORT_WIFI);
1952         if (hasInternet) {
1953             builder.addCapability(NET_CAPABILITY_INTERNET);
1954         } else {
1955             builder.removeCapability(NET_CAPABILITY_INTERNET);
1956         }
1957         return new Pair<String, NetworkRequest>(ssidKey,
1958                 builder.setNetworkSpecifier(specifierBuilder.build()).build());
1959     }
1960 
1961     @RequiresApi(Build.VERSION_CODES.S)
1962     @NonNull
buildCoexCellChannels()1963     private List<CoexUtils.CoexCellChannel> buildCoexCellChannels() {
1964         List<CoexUtils.CoexCellChannel> cellChannels = new ArrayList<>();
1965         while (getRemainingArgsCount() > 0) {
1966             final @Annotation.NetworkType int rat;
1967             final String ratArg = getNextArgRequired();
1968             if (TextUtils.equals(ratArg, "lte")) {
1969                 rat = TelephonyManager.NETWORK_TYPE_LTE;
1970             } else if (TextUtils.equals(ratArg, "nr")) {
1971                 rat = TelephonyManager.NETWORK_TYPE_NR;
1972             } else {
1973                 throw new IllegalArgumentException("Unknown rat type " + ratArg);
1974             }
1975             final int band = Integer.parseInt(getNextArgRequired());
1976             if (band < 1 || band > 261) {
1977                 throw new IllegalArgumentException("Band is " + band
1978                         + " but should be a value from 1 to 261");
1979             }
1980             final int downlinkFreqKhz = Integer.parseInt(getNextArgRequired());
1981             if (downlinkFreqKhz < 0 && downlinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) {
1982                 throw new IllegalArgumentException("Downlink frequency is " + downlinkFreqKhz
1983                         + " but should be >= 0 or UNKNOWN: "
1984                         + PhysicalChannelConfig.FREQUENCY_UNKNOWN);
1985             }
1986             final int downlinkBandwidthKhz = Integer.parseInt(getNextArgRequired());
1987             if (downlinkBandwidthKhz <= 0
1988                     && downlinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
1989                 throw new IllegalArgumentException("Downlink bandwidth is " + downlinkBandwidthKhz
1990                         + " but should be > 0 or UNKNOWN: "
1991                         + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN);
1992             }
1993             final int uplinkFreqKhz = Integer.parseInt(getNextArgRequired());
1994             if (uplinkFreqKhz < 0 && uplinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) {
1995                 throw new IllegalArgumentException("Uplink frequency is " + uplinkFreqKhz
1996                         + " but should be >= 0 or UNKNOWN: "
1997                         + PhysicalChannelConfig.FREQUENCY_UNKNOWN);
1998             }
1999             final int uplinkBandwidthKhz = Integer.parseInt(getNextArgRequired());
2000             if (uplinkBandwidthKhz <= 0
2001                     && uplinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
2002                 throw new IllegalArgumentException("Uplink bandwidth is " + uplinkBandwidthKhz
2003                         + " but should be > 0 or UNKNOWN: "
2004                         + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN);
2005             }
2006             cellChannels.add(new CoexUtils.CoexCellChannel(rat, band,
2007                     downlinkFreqKhz, downlinkBandwidthKhz, uplinkFreqKhz, uplinkBandwidthKhz,
2008                     SubscriptionManager.INVALID_SUBSCRIPTION_ID));
2009         }
2010         return cellChannels;
2011     }
2012 
setAutoJoin(PrintWriter pw, String ssid, boolean allowAutojoin)2013     private void setAutoJoin(PrintWriter pw, String ssid, boolean allowAutojoin) {
2014         // For suggestions, this will work only if the config has already been added
2015         // to WifiConfigManager.
2016         Bundle extras = new Bundle();
2017         if (SdkLevel.isAtLeastS()) {
2018             extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
2019                     mContext.getAttributionSource());
2020         }
2021         WifiConfiguration retrievedConfig =
2022                 mWifiService.getPrivilegedConfiguredNetworks(SHELL_PACKAGE_NAME, null, extras)
2023                         .getList()
2024                         .stream()
2025                         .filter(n -> n.SSID.equals(ssid))
2026                         .findAny()
2027                         .orElse(null);
2028         if (retrievedConfig == null) {
2029             pw.println("Cannot retrieve config, autojoin setting skipped.");
2030             return;
2031         }
2032         mWifiService.allowAutojoin(retrievedConfig.networkId, allowAutojoin);
2033     }
2034 
sendLinkProbe(PrintWriter pw)2035     private int sendLinkProbe(PrintWriter pw) throws InterruptedException {
2036         // Note: should match WifiNl80211Manager#SEND_MGMT_FRAME_TIMEOUT_MS
2037         final int sendMgmtFrameTimeoutMs = 1000;
2038 
2039         ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
2040         mWifiThreadRunner.post(() ->
2041                 mActiveModeWarden.getPrimaryClientModeManager().probeLink(new LinkProbeCallback() {
2042                     @Override
2043                     public void onAck(int elapsedTimeMs) {
2044                         queue.offer("Link probe succeeded after " + elapsedTimeMs + " ms");
2045                     }
2046 
2047                     @Override
2048                     public void onFailure(int reason) {
2049                         queue.offer("Link probe failed with reason "
2050                                 + LinkProbeCallback.failureReasonToString(reason));
2051                     }
2052                 }, -1));
2053 
2054         // block until msg is received, or timed out
2055         String msg = queue.poll(sendMgmtFrameTimeoutMs + 1000, TimeUnit.MILLISECONDS);
2056         if (msg == null) {
2057             pw.println("Link probe timed out");
2058         } else {
2059             pw.println(msg);
2060         }
2061         return 0;
2062     }
2063 
isApChannelMHzValid(PrintWriter pw, int apChannelMHz)2064     private boolean isApChannelMHzValid(PrintWriter pw, int apChannelMHz) {
2065         int[] allowed2gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
2066         int[] allowed5gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
2067         int[] allowed5gDfsFreq =
2068             mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
2069         int[] allowed6gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ);
2070         int[] allowed60gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_60_GHZ);
2071         if (allowed2gFreq == null) {
2072             allowed2gFreq = new int[0];
2073         }
2074         if (allowed5gFreq == null) {
2075             allowed5gFreq = new int[0];
2076         }
2077         if (allowed5gDfsFreq == null) {
2078             allowed5gDfsFreq = new int[0];
2079         }
2080         if (allowed6gFreq == null) {
2081             allowed6gFreq = new int[0];
2082         }
2083         if (allowed60gFreq == null) {
2084             allowed60gFreq = new int[0];
2085         }
2086         pw.println("2G freq: " + Arrays.toString(allowed2gFreq));
2087         pw.println("5G freq: " + Arrays.toString(allowed5gFreq));
2088         pw.println("5G DFS: " + Arrays.toString(allowed5gDfsFreq));
2089         pw.println("6G freq: " + Arrays.toString(allowed6gFreq));
2090         pw.println("60G freq: " + Arrays.toString(allowed60gFreq));
2091         return (Arrays.binarySearch(allowed2gFreq, apChannelMHz) >= 0
2092                 || Arrays.binarySearch(allowed5gFreq, apChannelMHz) >= 0
2093                 || Arrays.binarySearch(allowed5gDfsFreq, apChannelMHz) >= 0)
2094                 || Arrays.binarySearch(allowed6gFreq, apChannelMHz) >= 0
2095                 || Arrays.binarySearch(allowed60gFreq, apChannelMHz) >= 0;
2096     }
2097 
waitForWifiEnabled(boolean enabled)2098     private void waitForWifiEnabled(boolean enabled) throws InterruptedException {
2099         CountDownLatch countDownLatch = new CountDownLatch(1);
2100         BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
2101             @Override
2102             public void onReceive(Context context, Intent intent) {
2103                 String action = intent.getAction();
2104                 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
2105                     int state = mWifiService.getWifiEnabledState();
2106                     if ((enabled && state == WIFI_STATE_ENABLED)
2107                             || (!enabled && state == WIFI_STATE_DISABLED)) {
2108                         countDownLatch.countDown();
2109                     }
2110                 }
2111             }
2112         };
2113         IntentFilter filter = new IntentFilter();
2114         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
2115         mContext.registerReceiver(broadcastReceiver, filter);
2116         mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled);
2117         countDownLatch.await(5000, TimeUnit.MILLISECONDS);
2118         mContext.unregisterReceiver(broadcastReceiver);
2119     }
2120 
printWifiInfo(PrintWriter pw, WifiInfo info)2121     private void printWifiInfo(PrintWriter pw, WifiInfo info) {
2122         if (info.getSupplicantState() != SupplicantState.COMPLETED) {
2123             pw.println("Wifi is not connected");
2124             return;
2125         }
2126         pw.println("Wifi is connected to " + info.getSSID());
2127         pw.println("WifiInfo: " + info);
2128         // additional diagnostics not printed by WifiInfo.toString()
2129         pw.println("successfulTxPackets: " + info.txSuccess);
2130         pw.println("successfulTxPacketsPerSecond: " + info.getSuccessfulTxPacketsPerSecond());
2131         pw.println("retriedTxPackets: " + info.txRetries);
2132         pw.println("retriedTxPacketsPerSecond: " + info.getRetriedTxPacketsPerSecond());
2133         pw.println("lostTxPackets: " + info.txBad);
2134         pw.println("lostTxPacketsPerSecond: " + info.getLostTxPacketsPerSecond());
2135         pw.println("successfulRxPackets: " + info.rxSuccess);
2136         pw.println("successfulRxPacketsPerSecond: " + info.getSuccessfulRxPacketsPerSecond());
2137     }
2138 
printStatus(PrintWriter pw)2139     private void printStatus(PrintWriter pw) {
2140         boolean wifiEnabled = mWifiService.getWifiEnabledState() == WIFI_STATE_ENABLED;
2141         pw.println("Wifi is " + (wifiEnabled ? "enabled" : "disabled"));
2142         pw.println("Wifi scanning is "
2143                 + (mWifiService.isScanAlwaysAvailable()
2144                 ? "always available" : "only available when wifi is enabled"));
2145         if (!wifiEnabled) {
2146             return;
2147         }
2148         if (Binder.getCallingUid() != Process.ROOT_UID) {
2149             // not privileged, just dump the primary client mode manager manager status
2150             // (public API contents).
2151             pw.println("==== Primary ClientModeManager instance ====");
2152             printWifiInfo(pw, mWifiService.getConnectionInfo(SHELL_PACKAGE_NAME, null));
2153         } else {
2154             // privileged, dump out all the client mode manager manager statuses
2155             for (ClientModeManager cm : mActiveModeWarden.getClientModeManagers()) {
2156                 pw.println("==== ClientModeManager instance: " + cm + " ====");
2157                 WifiInfo info = cm.syncRequestConnectionInfo();
2158                 printWifiInfo(pw, info);
2159                 if (info.getSupplicantState() != SupplicantState.COMPLETED) {
2160                     continue;
2161                 }
2162                 Network network = cm.syncGetCurrentNetwork();
2163                 NetworkCapabilities capabilities =
2164                         mConnectivityManager.getNetworkCapabilities(network);
2165                 pw.println("NetworkCapabilities: " + capabilities);
2166             }
2167         }
2168     }
2169 
onHelpNonPrivileged(PrintWriter pw)2170     private void onHelpNonPrivileged(PrintWriter pw) {
2171         pw.println("  get-country-code");
2172         pw.println("    Gets country code as a two-letter string");
2173         pw.println("  set-wifi-enabled enabled|disabled");
2174         pw.println("    Enables/disables Wifi on this device.");
2175         pw.println("  set-scan-always-available enabled|disabled");
2176         pw.println("    Sets whether scanning should be available even when wifi is off.");
2177         pw.println("  list-scan-results");
2178         pw.println("    Lists the latest scan results");
2179         pw.println("  start-scan");
2180         pw.println("    Start a new scan");
2181         pw.println("  list-networks");
2182         pw.println("    Lists the saved networks");
2183         pw.println("  forget-network <networkId>");
2184         pw.println("    Remove the network mentioned by <networkId>");
2185         pw.println("        - Use list-networks to retrieve <networkId> for the network");
2186         pw.println("  status");
2187         pw.println("    Current wifi status");
2188         pw.println("  set-verbose-logging enabled|disabled ");
2189         pw.println("    Set the verbose logging enabled or disabled");
2190         pw.println("  is-verbose-logging");
2191         pw.println("    Check whether verbose logging enabled or disabled");
2192         pw.println("  start-restricting-auto-join-to-subscription-id subId");
2193         pw.println("    temporarily disable all wifi networks except merged carrier networks with"
2194                 + " the given subId");
2195         pw.println("  stop-restricting-auto-join-to-subscription-id");
2196         pw.println("    Undo the effects of "
2197                 + "start-restricting-auto-join-to-subscription-id");
2198         pw.println("  add-suggestion <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-u] [-o] [-p] [-m] "
2199                 + " [-s] [-d] [-b <bssid>] [-e] [-i] [-a <carrierId>] [-c <subscriptionId>]");
2200         pw.println("    Add a network suggestion with provided params");
2201         pw.println("    Use 'network-suggestions-set-user-approved " + SHELL_PACKAGE_NAME + " yes'"
2202                 +  " to approve suggestions added via shell (Needs root access)");
2203         pw.println("    <ssid> - SSID of the network");
2204         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
2205         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
2206         pw.println("           - 'open' - Open networks (Most prevalent)");
2207         pw.println("           - 'owe' - Enhanced open networks");
2208         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
2209         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
2210         pw.println("           - 'wpa3' - WPA-3 PSK networks");
2211         pw.println("    -u - Mark the suggestion untrusted.");
2212         pw.println("    -o - Mark the suggestion oem paid.");
2213         pw.println("    -p - Mark the suggestion oem private.");
2214         pw.println("    -m - Mark the suggestion metered.");
2215         pw.println("    -h - Mark the network hidden.");
2216         pw.println("    -s - Share the suggestion with user.");
2217         pw.println("    -d - Mark the suggestion autojoin disabled.");
2218         pw.println("    -b <bssid> - Set specific BSSID.");
2219         pw.println("    -r - Enable non_persistent randomization (disabled by default)");
2220         pw.println("    -a - Mark the suggestion carrier merged");
2221         pw.println("    -c <carrierId> - set carrier Id");
2222         pw.println("    -i <subscriptionId> - set subscription Id, if -a is used, "
2223                 + "this must be set");
2224         pw.println("  remove-suggestion <ssid> [-l]");
2225         pw.println("    Remove a network suggestion with provided SSID of the network");
2226         pw.println("    -l - Remove suggestion with lingering, if not set will disconnect "
2227                 + "immediately ");
2228         pw.println("  remove-all-suggestions");
2229         pw.println("    Removes all suggestions added via shell");
2230         pw.println("  list-suggestions");
2231         pw.println("    Lists the suggested networks added via shell");
2232         if (SdkLevel.isAtLeastS()) {
2233             pw.println("  set-coex-cell-channels [lte|nr <bandNumber 1-261> "
2234                     + "<downlinkFreqKhz or UNKNOWN: "
2235                     + PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> "
2236                     + "<downlinkBandwidthKhz or UNKNOWN: "
2237                     + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + "> "
2238                     + "<uplinkFreqKhz or UNKNOWN: "
2239                     + PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> "
2240                     + "<uplinkBandwidthKhz or UNKNOWN: "
2241                     + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + ">] ...");
2242             pw.println("    Sets a list of zero or more cell channels to use for coex calculations."
2243                     + " Actual device reported cell channels will be ignored until"
2244                     + " reset-coex-cell-channels is called.");
2245             pw.println("  reset-coex-cell-channels");
2246             pw.println("    Removes all cell channels set in set-coex-cell-channels and returns to "
2247                     + "listening on actual device reported cell channels");
2248             pw.println("  get-coex-cell-channels");
2249             pw.println("    Prints the cell channels being used for coex.");
2250         }
2251         pw.println("  set-connected-score <score>");
2252         pw.println("    Set connected wifi network score (to choose between LTE & Wifi for "
2253                 + "default route).");
2254         pw.println("    This turns off the active connected scorer (default or external).");
2255         pw.println("    Only works while connected to a wifi network. This score will stay in "
2256                 + "effect until you call reset-connected-score or the device disconnects from the "
2257                 + "current network.");
2258         pw.println("    <score> - Integer score should be in the range of 0 - 60");
2259         pw.println("  reset-connected-score");
2260         pw.println("    Turns on the default connected scorer.");
2261         pw.println("    Note: Will clear any external scorer set.");
2262         pw.println("  start-softap <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition) "
2263                 + "<passphrase> [-b 2|5|6|any|bridged]");
2264         pw.println("    Start softap with provided params");
2265         pw.println("    Note that the shell command doesn't activate internet tethering. In some "
2266                 + "devices, internet sharing is possible when Wi-Fi STA is also enabled and is"
2267                 + "associated to another AP with internet access.");
2268         pw.println("    <ssid> - SSID of the network");
2269         pw.println("    open|wpa2|wpa3|wpa3_transition|owe|owe_transition - Security type of the "
2270                 + "network.");
2271         pw.println("        - Use 'open', 'owe', 'owe_transition' for networks with no passphrase");
2272         pw.println("        - Use 'wpa2', 'wpa3', 'wpa3_transition' for networks with passphrase");
2273         pw.println("    -b 2|5|6|any|bridged - select the preferred band.");
2274         pw.println("        - Use '2' to select 2.4GHz band as the preferred band");
2275         pw.println("        - Use '5' to select 5GHz band as the preferred band");
2276         pw.println("        - Use '6' to select 6GHz band as the preferred band");
2277         pw.println("        - Use 'any' to indicate no band preference");
2278         pw.println("        - Use 'bridged' to indicate bridged AP which enables APs on both "
2279                 + "2.4G + 5G");
2280         pw.println("    Note: If the band option is not provided, 2.4GHz is the preferred band.");
2281         pw.println("          The exact channel is auto-selected by FW unless overridden by "
2282                 + "force-softap-channel command");
2283         pw.println("    -x - Specifies the SSID as hex digits instead of plain text (T and above)");
2284         pw.println("  stop-softap");
2285         pw.println("    Stop softap (hotspot)");
2286         pw.println("  pmksa-flush <networkId>");
2287         pw.println("        - Flush the local PMKSA cache associated with the network id."
2288                 + " Use list-networks to retrieve <networkId> for the network");
2289         pw.println("  reload-resources");
2290         pw.println(
2291                 "    Reset the WiFi resources cache which will cause them to be reloaded next "
2292                         + "time they are accessed. Necessary if overlays are manually modified.");
2293         pw.println("  launch-dialog-simple [-t <title>] [-m <message>]"
2294                 + " [-l <url> <url_start> <url_end>] [-y <positive_button_text>]"
2295                 + " [-n <negative_button_text>] [-x <neutral_button_text>] [-c <timeout_millis>]");
2296         pw.println("    Launches a simple dialog and waits up to 15 seconds to"
2297                 + " print the response.");
2298         pw.println("    -t - Title");
2299         pw.println("    -m - Message");
2300         pw.println("    -l - URL of the message, with the start and end index inside the message");
2301         pw.println("    -y - Positive Button Text");
2302         pw.println("    -n - Negative Button Text");
2303         pw.println("    -x - Neutral Button Text");
2304         pw.println("    -c - Optional timeout in milliseconds");
2305         pw.println("  launch-dialog-p2p-invitation-sent <device_name> <pin> [-i <display_id>]");
2306         pw.println("    Launches a P2P Invitation Sent dialog.");
2307         pw.println("    <device_name> - Name of the device the invitation was sent to");
2308         pw.println("    <pin> - PIN for the invited device to input");
2309         pw.println("  launch-dialog-p2p-invitation-received <device_name> [-p] [-d <pin>] "
2310                 + "[-i <display_id>] [-c <timeout_millis>]");
2311         pw.println("    Launches a P2P Invitation Received dialog and waits up to 15 seconds to"
2312                 + " print the response.");
2313         pw.println("    <device_name> - Name of the device sending the invitation");
2314         pw.println("    -p - Show PIN input");
2315         pw.println("    -d - Display PIN <pin>");
2316         pw.println("    -i - Display ID");
2317         pw.println("    -c - Optional timeout in milliseconds");
2318         pw.println("  query-interface <uid> <package_name> STA|AP|AWARE|DIRECT [-new]");
2319         pw.println(
2320                 "    Query whether the specified could be created for the specified UID and "
2321                         + "package name, and if so - what other interfaces would be destroyed");
2322         pw.println("    -new - query for a new interfaces (otherwise an existing interface is ok");
2323         pw.println("  interface-priority-interactive-mode enable|disable|default");
2324         pw.println("    Enable or disable asking the user when there's an interface priority "
2325                 + "conflict, |default| implies using the device default behavior.");
2326         pw.println("  set-one-shot-screen-on-delay-ms <delayMs>");
2327         pw.println("    set the delay for the next screen-on connectivity scan in milliseconds.");
2328         pw.println("  set-ipreach-disconnect enabled|disabled");
2329         pw.println("    Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.");
2330         pw.println("  get-ipreach-disconnect");
2331         pw.println("    Gets setting of CMD_IP_REACHABILITY_LOST events triggering disconnects.");
2332     }
2333 
onHelpPrivileged(PrintWriter pw)2334     private void onHelpPrivileged(PrintWriter pw) {
2335         pw.println("  connect-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-x] [-m] [-d] "
2336                 + "[-b <bssid>] [-r auto|none|persistent|non_persistent]");
2337         pw.println("    Connect to a network with provided params and add to saved networks list");
2338         pw.println("    <ssid> - SSID of the network");
2339         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
2340         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
2341         pw.println("           - 'open' - Open networks (Most prevalent)");
2342         pw.println("           - 'owe' - Enhanced open networks");
2343         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
2344         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
2345         pw.println("           - 'wpa3' - WPA-3 PSK networks");
2346         pw.println("    -x - Specifies the SSID as hex digits instead of plain text");
2347         pw.println("    -m - Mark the network metered.");
2348         pw.println("    -d - Mark the network autojoin disabled.");
2349         pw.println("    -h - Mark the network hidden.");
2350         pw.println("    -p - Mark the network private (not shared).");
2351         pw.println("    -b <bssid> - Set specific BSSID.");
2352         pw.println("    -r auto|none|persistent|non_persistent - MAC randomization scheme for the"
2353                 + " network");
2354         pw.println("  add-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-x] [-m] [-d] "
2355                 + "[-b <bssid>] [-r auto|none|persistent|non_persistent]");
2356         pw.println("    Add/update saved network with provided params");
2357         pw.println("    <ssid> - SSID of the network");
2358         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
2359         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
2360         pw.println("           - 'open' - Open networks (Most prevalent)");
2361         pw.println("           - 'owe' - Enhanced open networks");
2362         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
2363         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
2364         pw.println("           - 'wpa3' - WPA-3 PSK networks");
2365         pw.println("    -x - Specifies the SSID as hex digits instead of plain text");
2366         pw.println("    -m - Mark the network metered.");
2367         pw.println("    -d - Mark the network autojoin disabled.");
2368         pw.println("    -h - Mark the network hidden.");
2369         pw.println("    -p - Mark the network private (not shared).");
2370         pw.println("    -b <bssid> - Set specific BSSID.");
2371         pw.println("    -r auto|none|persistent|non_persistent - MAC randomization scheme for the"
2372                 + " network");
2373         pw.println("  set-poll-rssi-interval-msecs <int>");
2374         pw.println("    Sets the interval between RSSI polls to <int> milliseconds.");
2375         pw.println("  get-poll-rssi-interval-msecs");
2376         pw.println("    Gets current interval between RSSI polls, in milliseconds.");
2377         pw.println("  force-hi-perf-mode enabled|disabled");
2378         pw.println("    Sets whether hi-perf mode is forced or left for normal operation.");
2379         pw.println("  force-low-latency-mode enabled|disabled");
2380         pw.println("    Sets whether low latency mode is forced or left for normal operation.");
2381         pw.println("  network-suggestions-set-user-approved <package name> yes|no");
2382         pw.println("    Sets whether network suggestions from the app is approved or not.");
2383         pw.println("  network-suggestions-has-user-approved <package name>");
2384         pw.println("    Queries whether network suggestions from the app is approved or not.");
2385         pw.println("  imsi-protection-exemption-set-user-approved-for-carrier <carrier id> yes|no");
2386         pw.println("    Sets whether Imsi protection exemption for carrier is approved or not");
2387         pw.println("  imsi-protection-exemption-has-user-approved-for-carrier <carrier id>");
2388         pw.println("    Queries whether Imsi protection exemption for carrier is approved or not");
2389         pw.println("  imsi-protection-exemption-clear-user-approved-for-carrier <carrier id>");
2390         pw.println("    Clear the user choice on Imsi protection exemption for carrier");
2391         pw.println("  network-requests-remove-user-approved-access-points <package name>");
2392         pw.println("    Removes all user approved network requests for the app.");
2393         pw.println("  clear-user-disabled-networks");
2394         pw.println("    Clears the user disabled networks list.");
2395         pw.println("  send-link-probe");
2396         pw.println("    Manually triggers a link probe.");
2397         pw.println("  force-softap-band enabled <int> | disabled");
2398         pw.println("    Forces soft AP band to 2|5|6");
2399         pw.println("  force-softap-channel enabled <int> | disabled");
2400         pw.println("    Sets whether soft AP channel is forced to <int> MHz");
2401         pw.println("    or left for normal   operation.");
2402         pw.println("  force-country-code enabled <two-letter code> | disabled ");
2403         pw.println("    Sets country code to <two-letter code> or left for normal value");
2404         pw.println("    or '00' for forcing to world mode country code");
2405         pw.println("  set-wifi-watchdog enabled|disabled");
2406         pw.println("    Sets whether wifi watchdog should trigger recovery");
2407         pw.println("  get-wifi-watchdog");
2408         pw.println("    Gets setting of wifi watchdog trigger recovery.");
2409         pw.println("  get-softap-supported-features");
2410         pw.println("    Gets softap supported features. Will print 'wifi_softap_acs_supported'");
2411         pw.println("    and/or 'wifi_softap_wpa3_sae_supported',");
2412         pw.println("    and/or 'wifi_softap_bridged_ap_supported',");
2413         pw.println("    and/or 'wifi_softap_bridged_ap_with_sta_supported',");
2414         pw.println("    each on a separate line.");
2415         pw.println("  settings-reset");
2416         pw.println("    Initiates wifi settings reset");
2417         pw.println("  add-request [-g] [-i] [-n] [-s] <ssid> open|owe|wpa2|wpa3 [<passphrase>]"
2418                 + " [-b <bssid>] [-d <band=2|5|6|60>]");
2419         pw.println("    Add a network request with provided params");
2420         pw.println("    Use 'network-requests-set-user-approved android yes'"
2421                 +  " to pre-approve requests added via rooted shell (Not persisted)");
2422         pw.println("    -g - Marks the following SSID as a glob pattern");
2423         pw.println("    <ssid> - SSID of the network, or glob pattern if -g is present");
2424         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
2425         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
2426         pw.println("           - 'open' - Open networks (Most prevalent)");
2427         pw.println("           - 'owe' - Enhanced open networks");
2428         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
2429         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
2430         pw.println("           - 'wpa3' - WPA-3 PSK networks");
2431         pw.println("    -b <bssid> - Set specific BSSID.");
2432         pw.println("    -i Set internet capability.");
2433         pw.println("    -d Specify the band of access point: 2, 5, 6, or 60");
2434         pw.println("    -s No SSID provided, to be chosen by network selection.");
2435         pw.println("    -n - Prevent auto-selection of BSSID and force it to be null so that the "
2436                 + "request matches all BSSIDs.");
2437         pw.println("  remove-request <ssid>");
2438         pw.println("    Remove a network request with provided SSID of the network");
2439         pw.println("  remove-all-requests");
2440         pw.println("    Removes all active requests added via shell");
2441         pw.println("  list-requests");
2442         pw.println("    Lists the requested networks added via shell");
2443         pw.println("  network-requests-set-user-approved <package name> yes|no");
2444         pw.println("    Sets whether network requests from the app is approved or not.");
2445         pw.println("    Note: Only 1 such app can be approved from the shell at a time");
2446         pw.println("  network-requests-has-user-approved <package name>");
2447         pw.println("    Queries whether network requests from the app is approved or not.");
2448         pw.println("    Note: This only returns whether the app was set via the "
2449                 + "'network-requests-set-user-approved' shell command");
2450         pw.println("  list-all-suggestions");
2451         pw.println("    Lists all suggested networks on this device");
2452         pw.println("  list-suggestions-from-app <package name>");
2453         pw.println("    Lists the suggested networks from the app");
2454         pw.println("  set-emergency-callback-mode enabled|disabled");
2455         pw.println("    Sets whether Emergency Callback Mode (ECBM) is enabled.");
2456         pw.println("    Equivalent to receiving the "
2457                 + "TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED broadcast.");
2458         pw.println("  set-emergency-call-state enabled|disabled");
2459         pw.println("    Sets whether we are in the middle of an emergency call.");
2460         pw.println("Equivalent to receiving the "
2461                 + "TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED broadcast.");
2462         pw.println("  network-suggestions-set-as-carrier-provider <packageName> yes|no");
2463         pw.println("    Set the <packageName> work as carrier provider or not.");
2464         pw.println("  is-network-suggestions-set-as-carrier-provider <packageName>");
2465         pw.println("    Queries whether the <packageName> is working as carrier provider or not.");
2466         pw.println("  remove-app-from-suggestion_database <packageName>");
2467         pw.println("    Remove <packageName> from the suggestion database, all suggestions and user"
2468                 + " approval will be deleted, it is the same as uninstalling this app.");
2469         pw.println("  trigger-recovery");
2470         pw.println("    Trigger Wi-Fi subsystem restart.");
2471         pw.println("  start-faking-scans");
2472         pw.println("    Start faking scan results into the framework (configured with "
2473                 + "'add-fake-scan'), stop with 'stop-faking-scans'.");
2474         pw.println("  stop-faking-scans");
2475         pw.println("    Stop faking scan results - started with 'start-faking-scans'.");
2476         pw.println("  add-fake-scan [-x] <ssid> <bssid> <capabilities> <frequency> <dbm>");
2477         pw.println("    Add a fake scan result to be used when enabled via `start-faking-scans'.");
2478         pw.println("    Example WPA2: add-fake-scan fakeWpa2 80:01:02:03:04:05 "
2479                 + "\"[WPA2-PSK-CCMP][RSN-PSK-CCMP][ESS]\" 2412 -55");
2480         pw.println("    Example WPA3: add-fake-scan fakeWpa3 80:01:02:03:04:06 "
2481                 + "\"[RSN-SAE+FT/SAE-CCMP][ESS]\" 2412 -55");
2482         pw.println(
2483                 "    Example Open: add-fake-scan fakeOpen 80:01:02:03:04:07 \"[ESS]\" 2412 -55");
2484         pw.println("    Example OWE: add-fake-scan fakeOwe 80:01:02:03:04:08 \"[RSN-OWE-CCMP]\" "
2485                 + "2412 -55");
2486         pw.println(
2487                 "    Example WPA2/WPA3 transition mode: add-fake-scan fakeWpa2t3 80:01:02:03:04:09 "
2488                         + "\"[WPA2-PSK-CCMP][RSN-PSK+SAE-CCMP][ESS][MFPC]\" 2412 -55");
2489         pw.println(
2490                 "    Example Open/OWE transition mode: add-fake-scan fakeOpenOwe 80:01:02:03:04:0A "
2491                         + "\"[RSN-OWE_TRANSITION-CCMP][ESS]\" 2412 -55");
2492         pw.println(
2493                 "    Example Passpoint: add-fake-scan fakePasspoint 80:01:02:03:04:0B "
2494                         + "\"[WPA2-EAP/SHA1-CCMP][RSN-EAP/SHA1-CCMP][ESS][MFPR][MFPC]"
2495                         + "[PASSPOINT]\" 2412 -55");
2496         pw.println("    -x - Specifies the SSID as hex digits instead of plain text");
2497         pw.println("  reset-fake-scans");
2498         pw.println("    Resets all fake scan results added by 'add-fake-scan'.");
2499         pw.println("  enable-scanning enabled|disabled [-h]");
2500         pw.println("    Sets whether all scanning should be enabled or disabled");
2501         pw.println("    -h - Enable scanning for hidden networks.");
2502         pw.println("  set-passpoint-enabled enabled|disabled");
2503         pw.println("    Sets whether Passpoint should be enabled or disabled");
2504         pw.println("  start-lohs <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition) "
2505                 + "<passphrase> [-b 2|5|6|any]");
2506         pw.println("    Start local only softap (hotspot) with provided params");
2507         pw.println("    <ssid> - SSID of the network");
2508         pw.println("    open|wpa2|wpa3|wpa3_transition|owe|owe_transition - Security type of the "
2509                 + "network.");
2510         pw.println("        - Use 'open', 'owe', 'owe_transition' for networks with no passphrase");
2511         pw.println("        - Use 'wpa2', 'wpa3', 'wpa3_transition' for networks with passphrase");
2512         pw.println("    -b 2|5|6|any|bridged - select the preferred band.");
2513         pw.println("        - Use '2' to select 2.4GHz band as the preferred band");
2514         pw.println("        - Use '5' to select 5GHz band as the preferred band");
2515         pw.println("        - Use '6' to select 6GHz band as the preferred band");
2516         pw.println("        - Use 'any' to indicate no band preference");
2517         pw.println("        - Use 'bridged' to indicate bridged AP which enables APs on both "
2518                 + "2.4G + 5G");
2519         pw.println("    Note: If the band option is not provided, 2.4GHz is the preferred band.");
2520         pw.println("  stop-lohs");
2521         pw.println("    Stop local only softap (hotspot)");
2522         pw.println("  set-multi-internet-mode 0|1|2");
2523         pw.println("    Sets Multi Internet use case mode. 0-disabled 1-dbs 2-multi ap");
2524         pw.println("  set-pno-request <ssid> [-f <frequency>]");
2525         pw.println("    Requests to include a non-quoted UTF-8 SSID in PNO scans");
2526         pw.println("  clear-pno-request");
2527         pw.println("    Clear the PNO scan request.");
2528         pw.println("  start-dpp-enrollee-responder [-i <info>] [-c <curve>]");
2529         pw.println("    Start DPP Enrollee responder mode.");
2530         pw.println("    -i - Device Info to be used in DPP Bootstrapping URI");
2531         pw.println("    -c - Cryptography Curve integer 1:p256v1, 2:s384r1, etc");
2532         pw.println("  start-dpp-configurator-initiator <networkId> <netRole> <enrolleeURI>");
2533         pw.println("    Start DPP Configurator Initiator mode.");
2534         pw.println("    netRole - 0: STA, 1: AP");
2535         pw.println("    enrolleeURI - Bootstrapping URI received from Enrollee");
2536         pw.println("  stop-dpp");
2537         pw.println("    Stop DPP session.");
2538     }
2539 
2540     @Override
onHelp()2541     public void onHelp() {
2542         final PrintWriter pw = getOutPrintWriter();
2543         pw.println("Wi-Fi (wifi) commands:");
2544         pw.println("  help or -h");
2545         pw.println("    Print this help text.");
2546         onHelpNonPrivileged(pw);
2547         if (Binder.getCallingUid() == Process.ROOT_UID) {
2548             onHelpPrivileged(pw);
2549         }
2550         pw.println();
2551     }
2552 
printWifiNetworkSuggestions(PrintWriter pw, Collection<WifiNetworkSuggestion> suggestions)2553     private void printWifiNetworkSuggestions(PrintWriter pw,
2554             Collection<WifiNetworkSuggestion> suggestions) {
2555         if (suggestions == null || suggestions.isEmpty()) {
2556             pw.println("No suggestions on this device");
2557         } else {
2558             pw.println("SSID                         Security type(s)");
2559             for (WifiNetworkSuggestion suggestion : suggestions) {
2560                 pw.println(String.format("%-32s %-4s",
2561                         WifiInfo.sanitizeSsid(suggestion.getWifiConfiguration().SSID),
2562                         suggestion.getWifiConfiguration().getSecurityParamsList().stream()
2563                                 .map(p -> WifiConfiguration.getSecurityTypeName(
2564                                         p.getSecurityType())
2565                                         + (p.isAddedByAutoUpgrade() ? "^" : ""))
2566                                 .collect(Collectors.joining("/"))));
2567             }
2568         }
2569     }
2570 }
2571