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.VERBOSE_LOGGING_LEVEL_DISABLED;
29 import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY;
30 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
31 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
32 
33 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP;
34 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE;
35 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_NAN;
36 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_P2P;
37 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA;
38 import static com.android.server.wifi.SelfRecovery.REASON_API_CALL;
39 
40 import android.content.BroadcastReceiver;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.IntentFilter;
44 import android.hardware.display.DisplayManager;
45 import android.location.Location;
46 import android.location.LocationManager;
47 import android.net.ConnectivityManager;
48 import android.net.MacAddress;
49 import android.net.Network;
50 import android.net.NetworkCapabilities;
51 import android.net.NetworkRequest;
52 import android.net.wifi.IActionListener;
53 import android.net.wifi.IDppCallback;
54 import android.net.wifi.ILastCallerListener;
55 import android.net.wifi.ILocalOnlyHotspotCallback;
56 import android.net.wifi.IPnoScanResultsCallback;
57 import android.net.wifi.IScoreUpdateObserver;
58 import android.net.wifi.ISoftApCallback;
59 import android.net.wifi.IWifiConnectedNetworkScorer;
60 import android.net.wifi.ScanResult;
61 import android.net.wifi.SoftApCapability;
62 import android.net.wifi.SoftApConfiguration;
63 import android.net.wifi.SoftApInfo;
64 import android.net.wifi.SoftApState;
65 import android.net.wifi.SupplicantState;
66 import android.net.wifi.WifiAvailableChannel;
67 import android.net.wifi.WifiClient;
68 import android.net.wifi.WifiConfiguration;
69 import android.net.wifi.WifiConnectedSessionInfo;
70 import android.net.wifi.WifiContext;
71 import android.net.wifi.WifiInfo;
72 import android.net.wifi.WifiManager;
73 import android.net.wifi.WifiNetworkSelectionConfig;
74 import android.net.wifi.WifiNetworkSpecifier;
75 import android.net.wifi.WifiNetworkSuggestion;
76 import android.net.wifi.WifiScanner;
77 import android.net.wifi.WifiSsid;
78 import android.net.wifi.util.ScanResultUtil;
79 import android.net.wifi.util.WifiResourceCache;
80 import android.os.Binder;
81 import android.os.Build;
82 import android.os.Bundle;
83 import android.os.PatternMatcher;
84 import android.os.Process;
85 import android.os.RemoteException;
86 import android.os.SystemClock;
87 import android.os.WorkSource;
88 import android.telephony.Annotation;
89 import android.telephony.PhysicalChannelConfig;
90 import android.telephony.SubscriptionManager;
91 import android.telephony.TelephonyManager;
92 import android.text.TextUtils;
93 import android.util.Log;
94 import android.util.Pair;
95 import android.util.SparseArray;
96 import android.util.SparseIntArray;
97 import android.view.Display;
98 
99 import androidx.annotation.NonNull;
100 import androidx.annotation.Nullable;
101 import androidx.annotation.RequiresApi;
102 
103 import com.android.internal.annotations.VisibleForTesting;
104 import com.android.modules.utils.BasicShellCommandHandler;
105 import com.android.modules.utils.ParceledListSlice;
106 import com.android.modules.utils.build.SdkLevel;
107 import com.android.server.wifi.ClientMode.LinkProbeCallback;
108 import com.android.server.wifi.coex.CoexManager;
109 import com.android.server.wifi.coex.CoexUtils;
110 import com.android.server.wifi.hal.WifiChip;
111 import com.android.server.wifi.hotspot2.NetworkDetail;
112 import com.android.server.wifi.util.ApConfigUtil;
113 import com.android.server.wifi.util.ArrayUtils;
114 
115 import libcore.util.HexEncoding;
116 
117 import java.io.PrintWriter;
118 import java.nio.charset.Charset;
119 import java.nio.charset.StandardCharsets;
120 import java.util.ArrayList;
121 import java.util.Arrays;
122 import java.util.Collection;
123 import java.util.Collections;
124 import java.util.Comparator;
125 import java.util.HashMap;
126 import java.util.List;
127 import java.util.Map;
128 import java.util.Set;
129 import java.util.concurrent.ArrayBlockingQueue;
130 import java.util.concurrent.ConcurrentHashMap;
131 import java.util.concurrent.CountDownLatch;
132 import java.util.concurrent.TimeUnit;
133 import java.util.stream.Collectors;
134 
135 /**
136  * Interprets and executes 'adb shell cmd wifi [args]'.
137  *
138  * To add new commands:
139  * - onCommand: Add a case "<command>" execute. Return a 0
140  *   if command executed successfully.
141  * - onHelp: add a description string.
142  *
143  * Permissions: currently root permission is required for some commands. Others will
144  * enforce the corresponding API permissions.
145  */
146 public class WifiShellCommand extends BasicShellCommandHandler {
147     @VisibleForTesting
148     public static String SHELL_PACKAGE_NAME = "com.android.shell";
149 
150     // These don't require root access.
151     // However, these do perform permission checks in the corresponding WifiService methods.
152     private static final String[] NON_PRIVILEGED_COMMANDS = {
153             "add-suggestion",
154             "forget-network",
155             "connect-network",
156             "add-network",
157             "get-country-code",
158             "help",
159             "-h",
160             "is-verbose-logging",
161             "list-scan-results",
162             "list-networks",
163             "list-suggestions",
164             "remove-suggestion",
165             "remove-all-suggestions",
166             "reset-connected-score",
167             "set-connected-score",
168             "set-scan-always-available",
169             "set-verbose-logging",
170             "set-wifi-enabled",
171             "set-passpoint-enabled",
172             "set-multi-internet-state",
173             "start-scan",
174             "status",
175             "query-interface",
176             "interface-priority-interactive-mode",
177             "set-one-shot-screen-on-delay-ms",
178             "set-network-selection-config",
179             "set-ipreach-disconnect",
180             "get-ipreach-disconnect",
181             "take-bugreport",
182             "get-allowed-channel",
183             "set-mock-wifimodem-service",
184             "get-mock-wifimodem-service",
185             "set-mock-wifimodem-methods",
186             "force-overlay-config-value",
187             "get-softap-supported-features",
188             "get-overlay-config-values"
189     };
190 
191     private static final Map<String, Pair<NetworkRequest, ConnectivityManager.NetworkCallback>>
192             sActiveRequests = new ConcurrentHashMap<>();
193 
194     private final ActiveModeWarden mActiveModeWarden;
195     private final WifiGlobals mWifiGlobals;
196     private final WifiLockManager mWifiLockManager;
197     private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
198     private final WifiConfigManager mWifiConfigManager;
199     private final WifiNative mWifiNative;
200     private final CoexManager mCoexManager;
201     private final WifiCountryCode mWifiCountryCode;
202     private final WifiLastResortWatchdog mWifiLastResortWatchdog;
203     private final WifiServiceImpl mWifiService;
204     private final WifiContext mContext;
205     private final ConnectivityManager mConnectivityManager;
206     private final WifiCarrierInfoManager mWifiCarrierInfoManager;
207     private final WifiNetworkFactory mWifiNetworkFactory;
208     private final SelfRecovery mSelfRecovery;
209     private final WifiThreadRunner mWifiThreadRunner;
210     private final WifiApConfigStore mWifiApConfigStore;
211     private int mSapState = WifiManager.WIFI_STATE_UNKNOWN;
212     private final ScanRequestProxy mScanRequestProxy;
213     private final @NonNull WifiDialogManager mWifiDialogManager;
214     private final HalDeviceManager mHalDeviceManager;
215     private final InterfaceConflictManager mInterfaceConflictManager;
216     private final SsidTranslator mSsidTranslator;
217     private final WifiDiagnostics mWifiDiagnostics;
218     private final DeviceConfigFacade mDeviceConfig;
219     private final AfcManager mAfcManager;
220     private final WifiInjector mWifiInjector;
221     private static final int[] OP_MODE_LIST = {
222             WifiAvailableChannel.OP_MODE_STA,
223             WifiAvailableChannel.OP_MODE_SAP,
224             WifiAvailableChannel.OP_MODE_WIFI_DIRECT_CLI,
225             WifiAvailableChannel.OP_MODE_WIFI_DIRECT_GO,
226             WifiAvailableChannel.OP_MODE_WIFI_AWARE,
227             WifiAvailableChannel.OP_MODE_TDLS,
228     };
229 
230     private class SoftApCallbackProxy extends ISoftApCallback.Stub {
231         private final PrintWriter mPrintWriter;
232         private final CountDownLatch mCountDownLatch;
233 
SoftApCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch)234         SoftApCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch) {
235             mPrintWriter = printWriter;
236             mCountDownLatch = countDownLatch;
237         }
238 
239         @Override
onStateChanged(SoftApState state)240         public void onStateChanged(SoftApState state) {
241             mPrintWriter.println("onStateChanged with state: " + state);
242 
243             mSapState = state.getState();
244             if (mSapState == WifiManager.WIFI_AP_STATE_ENABLED) {
245                 mPrintWriter.println(" SAP is enabled successfully");
246                 // Skip countDown() and wait for onInfoChanged() which has
247                 // the confirmed softAp channel information
248             } else if (mSapState == WifiManager.WIFI_AP_STATE_DISABLED) {
249                 mPrintWriter.println(" SAP is disabled");
250             } else if (mSapState == WifiManager.WIFI_AP_STATE_FAILED) {
251                 mPrintWriter.println(" SAP failed to start");
252                 mCountDownLatch.countDown();
253             }
254         }
255 
256         @Override
onConnectedClientsOrInfoChanged(Map<String, SoftApInfo> infos, Map<String, List<WifiClient>> clients, boolean isBridged, boolean isRegistration)257         public void onConnectedClientsOrInfoChanged(Map<String, SoftApInfo> infos,
258                 Map<String, List<WifiClient>> clients, boolean isBridged,
259                 boolean isRegistration) {
260             mPrintWriter.println("onConnectedClientsOrInfoChanged, infos: " + infos
261                     + ", clients: " + clients + ", isBridged: " + isBridged);
262             if (mSapState == WifiManager.WIFI_AP_STATE_ENABLED && infos.size() != 0) {
263                 mCountDownLatch.countDown();
264             }
265         }
266 
267         @Override
onCapabilityChanged(SoftApCapability capability)268         public void onCapabilityChanged(SoftApCapability capability) {
269             mPrintWriter.println("onCapabilityChanged " + capability);
270         }
271 
272         @Override
onBlockedClientConnecting(WifiClient client, int reason)273         public void onBlockedClientConnecting(WifiClient client, int reason) {
274         }
275     }
276 
277     /**
278      * Used for shell command testing of DPP feature.
279      */
280     public static class DppCallbackProxy extends IDppCallback.Stub {
281         private final PrintWriter mPrintWriter;
282         private final CountDownLatch mCountDownLatch;
283         private static final int STATUS_SUCCESS = 0;
284         private static final int STATUS_PROGRESS = 1;
285         private static final int STATUS_FAILURE = 2;
286 
DppCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch)287         DppCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch) {
288             mPrintWriter = printWriter;
289             mCountDownLatch = countDownLatch;
290         }
291 
292         @Override
onSuccessConfigReceived(int networkId)293         public void onSuccessConfigReceived(int networkId) {
294             mPrintWriter.println("onSuccessConfigReceived. netId=" + networkId);
295             mCountDownLatch.countDown();
296         }
297 
298         @Override
onSuccess(int status)299         public void onSuccess(int status) {
300             mPrintWriter.println("onSuccess status=" + statusToString(STATUS_SUCCESS, status));
301             mCountDownLatch.countDown();
302         }
303 
304         @Override
onFailure(int status, String ssid, String channelList, int[] bandArray)305         public void onFailure(int status, String ssid, String channelList, int[] bandArray) {
306             mPrintWriter.println("onFailure. status=" + statusToString(STATUS_FAILURE, status)
307                     + "ssid=" + ssid + "channelList=" + channelList);
308             mCountDownLatch.countDown();
309         }
310 
311         @Override
onProgress(int status)312         public void onProgress(int status) {
313             mPrintWriter.println("onProgress status=" + statusToString(STATUS_PROGRESS, status));
314         }
315 
316         @Override
onBootstrapUriGenerated(String uri)317         public void onBootstrapUriGenerated(String uri) {
318             mPrintWriter.println("onBootstrapUriGenerated URI = " + uri);
319         }
320 
statusToString(int type, int status)321         private String statusToString(int type, int status) {
322             switch (type) {
323                 case STATUS_SUCCESS: {
324                     switch (status) {
325                         case 0:
326                             return "CONFIGURATION_SENT";
327                         case 1:
328                             return "CONFIGURATION_APPLIED";
329                         default:
330                             return "Unknown success code";
331                     }
332                 }
333                 case STATUS_PROGRESS: {
334                     switch (status) {
335                         case 0:
336                             return "AUTHENTICATION_SUCCESS";
337                         case 1:
338                             return "RESPONSE_PENDING";
339                         case 2:
340                             return "CONFIGURATION_SENT_WAITING_RESPONSE";
341                         case 3:
342                             return "CONFIGURATION_ACCEPTED";
343                         default:
344                             return "Unknown progress code";
345                     }
346                 }
347                 case STATUS_FAILURE: {
348                     switch (status) {
349                         case -1:
350                             return "INVALID_URI";
351                         case -2:
352                             return "AUTHENTICATION";
353                         case -3:
354                             return "NOT_COMPATIBLE";
355                         case -4:
356                             return "CONFIGURATION";
357                         case -5:
358                             return "BUSY";
359                         case -6:
360                             return "TIMEOUT";
361                         case -7:
362                             return "GENERIC";
363                         case -8:
364                             return "NOT_SUPPORTED";
365                         case -9:
366                             return "INVALID_NETWORK";
367                         case -10:
368                             return "CANNOT_FIND_NETWORK";
369                         case -11:
370                             return "ENROLLEE_AUTHENTICATION";
371                         case -12:
372                             return "ENROLLEE_REJECTED_CONFIGURATION";
373                         case -13:
374                             return "URI_GENERATION";
375                         case -14:
376                             return "ENROLLEE_FAILED_TO_SCAN_NETWORK_CHANNEL";
377                         default:
378                             return "Unknown failure code";
379                     }
380                 }
381                 default :
382                     return "Unknown status type";
383             }
384         }
385     }
386 
387     /**
388      * Used for shell command testing of scorer.
389      */
390     public static class WifiScorer extends IWifiConnectedNetworkScorer.Stub {
391         private final WifiServiceImpl mWifiService;
392         private final CountDownLatch mCountDownLatch;
393         private Integer mSessionId;
394         private IScoreUpdateObserver mScoreUpdateObserver;
395 
WifiScorer(WifiServiceImpl wifiService, CountDownLatch countDownLatch)396         public WifiScorer(WifiServiceImpl wifiService, CountDownLatch countDownLatch) {
397             mWifiService = wifiService;
398             mCountDownLatch  = countDownLatch;
399         }
400 
401         @Override
onStart(WifiConnectedSessionInfo sessionInfo)402         public void onStart(WifiConnectedSessionInfo sessionInfo) {
403             mSessionId = sessionInfo.getSessionId();
404             mCountDownLatch.countDown();
405         }
406         @Override
onStop(int sessionId)407         public void onStop(int sessionId) {
408             // clear the external scorer on disconnect.
409             mWifiService.clearWifiConnectedNetworkScorer();
410         }
411         @Override
onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl)412         public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) {
413             mScoreUpdateObserver = observerImpl;
414             mCountDownLatch.countDown();
415         }
416         @Override
onNetworkSwitchAccepted( int sessionId, int targetNetworkId, String targetBssid)417         public void onNetworkSwitchAccepted(
418                 int sessionId, int targetNetworkId, String targetBssid) {
419             Log.i(TAG, "onNetworkSwitchAccepted:"
420                     + " sessionId=" + sessionId
421                     + " targetNetworkId=" + targetNetworkId
422                     + " targetBssid=" + targetBssid);
423         }
424 
425         @Override
onNetworkSwitchRejected( int sessionId, int targetNetworkId, String targetBssid)426         public void onNetworkSwitchRejected(
427                 int sessionId, int targetNetworkId, String targetBssid) {
428             Log.i(TAG, "onNetworkSwitchRejected:"
429                     + " sessionId=" + sessionId
430                     + " targetNetworkId=" + targetNetworkId
431                     + " targetBssid=" + targetBssid);
432         }
433 
getSessionId()434         public Integer getSessionId() {
435             return mSessionId;
436         }
437 
getScoreUpdateObserver()438         public IScoreUpdateObserver getScoreUpdateObserver() {
439             return mScoreUpdateObserver;
440         }
441     }
442 
WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, WifiContext context, WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner)443     WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, WifiContext context,
444             WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner) {
445         mWifiInjector = wifiInjector;
446         mWifiGlobals = wifiGlobals;
447         mWifiThreadRunner = wifiThreadRunner;
448         mActiveModeWarden = wifiInjector.getActiveModeWarden();
449         mWifiLockManager = wifiInjector.getWifiLockManager();
450         mWifiNetworkSuggestionsManager = wifiInjector.getWifiNetworkSuggestionsManager();
451         mWifiConfigManager = wifiInjector.getWifiConfigManager();
452         mWifiNative = wifiInjector.getWifiNative();
453         mCoexManager = wifiInjector.getCoexManager();
454         mWifiCountryCode = wifiInjector.getWifiCountryCode();
455         mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
456         mWifiService = wifiService;
457         mContext = context;
458         mConnectivityManager = context.getSystemService(ConnectivityManager.class);
459         mWifiCarrierInfoManager = wifiInjector.getWifiCarrierInfoManager();
460         mWifiNetworkFactory = wifiInjector.getWifiNetworkFactory();
461         mSelfRecovery = wifiInjector.getSelfRecovery();
462         mWifiApConfigStore = wifiInjector.getWifiApConfigStore();
463         mScanRequestProxy = wifiInjector.getScanRequestProxy();
464         mWifiDialogManager = wifiInjector.getWifiDialogManager();
465         mHalDeviceManager = wifiInjector.getHalDeviceManager();
466         mInterfaceConflictManager = wifiInjector.getInterfaceConflictManager();
467         mSsidTranslator = wifiInjector.getSsidTranslator();
468         mWifiDiagnostics = wifiInjector.getWifiDiagnostics();
469         mDeviceConfig = wifiInjector.getDeviceConfigFacade();
470         mAfcManager = wifiInjector.getAfcManager();
471     }
472 
getOpModeName(@ifiAvailableChannel.OpMode int mode)473     private String getOpModeName(@WifiAvailableChannel.OpMode int mode) {
474         switch (mode) {
475             case WifiAvailableChannel.OP_MODE_STA:
476                 return "STA";
477             case WifiAvailableChannel.OP_MODE_SAP:
478                 return "SAP";
479             case WifiAvailableChannel.OP_MODE_WIFI_DIRECT_CLI:
480                 return "WiFi-Direct GC";
481             case WifiAvailableChannel.OP_MODE_WIFI_DIRECT_GO:
482                 return "WiFi-Direct GO";
483             case WifiAvailableChannel.OP_MODE_WIFI_AWARE:
484                 return "WiFi-Aware";
485             case WifiAvailableChannel.OP_MODE_TDLS:
486                 return "TDLS";
487             default:
488                 return "";
489         }
490     }
491 
492     @Override
onCommand(String cmd)493     public int onCommand(String cmd) {
494         // Treat no command as help command.
495         if (TextUtils.isEmpty(cmd)) {
496             cmd = "help";
497         }
498         // Explicit exclusion from root permission
499         if (ArrayUtils.indexOf(NON_PRIVILEGED_COMMANDS, cmd) == -1) {
500             final int uid = Binder.getCallingUid();
501             if (uid != Process.ROOT_UID) {
502                 throw new SecurityException(
503                         "Uid " + uid + " does not have access to " + cmd + " wifi command "
504                                 + "(or such command doesn't exist)");
505             }
506         }
507         final PrintWriter pw = getOutPrintWriter();
508         try {
509             switch (cmd) {
510                 case "set-ipreach-disconnect": {
511                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
512                     mWifiGlobals.setIpReachabilityDisconnectEnabled(enabled);
513                     return 0;
514                 }
515                 case "get-ipreach-disconnect":
516                     pw.println("IPREACH_DISCONNECT state is "
517                             + mWifiGlobals.getIpReachabilityDisconnectEnabled());
518                     return 0;
519                 case "set-poll-rssi-interval-msecs":
520                     int newPollIntervalMsecs;
521                     try {
522                         newPollIntervalMsecs = Integer.parseInt(getNextArgRequired());
523                     } catch (NumberFormatException e) {
524                         pw.println(
525                                 "Invalid argument to 'set-poll-rssi-interval-msecs' "
526                                         + "- must be a positive integer");
527                         return -1;
528                     }
529 
530                     if (newPollIntervalMsecs < 1) {
531                         pw.println(
532                                 "Invalid argument to 'set-poll-rssi-interval-msecs' "
533                                         + "- must be a positive integer");
534                         return -1;
535                     }
536 
537                     mWifiGlobals.setPollRssiIntervalMillis(newPollIntervalMsecs);
538                     return 0;
539                 case "get-poll-rssi-interval-msecs":
540                     pw.println("WifiGlobals.getPollRssiIntervalMillis() = "
541                             + mWifiGlobals.getPollRssiIntervalMillis());
542                     return 0;
543                 case "force-hi-perf-mode": {
544                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
545                     if (!mWifiLockManager.forceHiPerfMode(enabled)) {
546                         pw.println("Command execution failed");
547                     }
548                     return 0;
549                 }
550                 case "force-low-latency-mode": {
551                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
552                     if (!mWifiLockManager.forceLowLatencyMode(enabled)) {
553                         pw.println("Command execution failed");
554                     }
555                     return 0;
556                 }
557                 case "network-suggestions-set-user-approved": {
558                     String packageName = getNextArgRequired();
559                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
560                     mWifiThreadRunner.post(() -> mWifiNetworkSuggestionsManager
561                             .setHasUserApprovedForApp(approved,
562                                     Binder.getCallingUid(), packageName),
563                             "shell#setHasUserApprovedForApp");
564                     return 0;
565                 }
566                 case "network-suggestions-has-user-approved": {
567                     String packageName = getNextArgRequired();
568                     boolean hasUserApproved =
569                             mWifiNetworkSuggestionsManager.hasUserApprovedForApp(packageName);
570                     pw.println(hasUserApproved ? "yes" : "no");
571                     return 0;
572                 }
573                 case "imsi-protection-exemption-set-user-approved-for-carrier": {
574                     String arg1 = getNextArgRequired();
575                     int carrierId = -1;
576                     try {
577                         carrierId = Integer.parseInt(arg1);
578                     } catch (NumberFormatException e) {
579                         pw.println("Invalid argument to "
580                                 + "'imsi-protection-exemption-set-user-approved-for-carrier' "
581                                 + "- carrierId must be an Integer");
582                         return -1;
583                     }
584                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
585                     mWifiCarrierInfoManager
586                             .setHasUserApprovedImsiPrivacyExemptionForCarrier(approved, carrierId);
587                     return 0;
588                 }
589                 case "imsi-protection-exemption-has-user-approved-for-carrier": {
590                     String arg1 = getNextArgRequired();
591                     int carrierId = -1;
592                     try {
593                         carrierId = Integer.parseInt(arg1);
594                     } catch (NumberFormatException e) {
595                         pw.println("Invalid argument to "
596                                 + "'imsi-protection-exemption-has-user-approved-for-carrier' "
597                                 + "- 'carrierId' must be an Integer");
598                         return -1;
599                     }
600                     boolean hasUserApproved = mWifiCarrierInfoManager
601                             .hasUserApprovedImsiPrivacyExemptionForCarrier(carrierId);
602                     pw.println(hasUserApproved ? "yes" : "no");
603                     return 0;
604                 }
605                 case "imsi-protection-exemption-clear-user-approved-for-carrier": {
606                     String arg1 = getNextArgRequired();
607                     try {
608                         final int carrierId = Integer.parseInt(arg1);
609                         mWifiThreadRunner.post(() ->
610                                 mWifiCarrierInfoManager.clearImsiPrivacyExemptionForCarrier(
611                                         carrierId), TAG + "#" + cmd);
612                     } catch (NumberFormatException e) {
613                         pw.println("Invalid argument to "
614                                 + "'imsi-protection-exemption-clear-user-approved-for-carrier' "
615                                 + "- 'carrierId' must be an Integer");
616                         return -1;
617                     }
618                     return 0;
619                 }
620                 case "network-requests-remove-user-approved-access-points": {
621                     String packageName = getNextArgRequired();
622                     mWifiThreadRunner.post(() -> mWifiNetworkFactory.removeApp(packageName),
623                             TAG + "#" + cmd);
624                     return 0;
625                 }
626                 case "clear-user-disabled-networks": {
627                     mWifiConfigManager.clearUserTemporarilyDisabledList();
628                     return 0;
629                 }
630                 case "send-link-probe": {
631                     return sendLinkProbe(pw);
632                 }
633                 case "get-last-caller-info": {
634                     int apiType = Integer.parseInt(getNextArgRequired());
635                     mWifiService.getLastCallerInfoForApi(apiType,
636                             new ILastCallerListener.Stub() {
637                                 @Override
638                                 public void onResult(String packageName, boolean enabled) {
639                                     Log.i(TAG, "getLastCallerInfoForApi " + apiType
640                                             + ": packageName=" + packageName
641                                             + ", enabled=" + enabled);
642                                 }
643                             });
644                     return 0;
645                 }
646                 case "force-softap-band": {
647                     boolean forceBandEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
648                     if (forceBandEnabled) {
649                         String forcedBand = getNextArgRequired();
650                         if (forcedBand.equals("2")) {
651                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
652                                     SoftApConfiguration.BAND_2GHZ, 0,
653                                     SoftApInfo.CHANNEL_WIDTH_AUTO);
654                         } else if (forcedBand.equals("5")) {
655                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
656                                     SoftApConfiguration.BAND_5GHZ, 0,
657                                     SoftApInfo.CHANNEL_WIDTH_AUTO);
658                         } else if (forcedBand.equals("6")) {
659                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
660                                     SoftApConfiguration.BAND_6GHZ, 0,
661                                     SoftApInfo.CHANNEL_WIDTH_AUTO);
662                         } else {
663                             pw.println("Invalid argument to 'force-softap-band enabled' "
664                                     + "- must be a valid band integer (2|5|6)");
665                             return -1;
666                         }
667                         return 0;
668                     } else {
669                         mWifiApConfigStore.disableForceSoftApBandOrChannel();
670                         return 0;
671                     }
672 
673                 }
674                 case "force-softap-channel": {
675                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
676                     if (enabled) {
677                         int apChannelMHz;
678                         int apMaxBandWidthMHz = 0;
679                         try {
680                             apChannelMHz = Integer.parseInt(getNextArgRequired());
681                             String option = getNextOption();
682                             if (option != null && option.equals("-w")) {
683                                 if (!SdkLevel.isAtLeastT()) {
684                                     pw.println("Maximum channel bandwidth can be set only on"
685                                             + " SdkLevel T or later.");
686                                     return -1;
687                                 }
688                                 String bandwidthStr = getNextArgRequired();
689                                 try {
690                                     apMaxBandWidthMHz = Integer.parseInt(bandwidthStr);
691                                 } catch (NumberFormatException e) {
692                                     pw.println("Invalid maximum channel bandwidth arg: "
693                                             + bandwidthStr);
694                                     return -1;
695                                 }
696                             }
697                         } catch (NumberFormatException e) {
698                             pw.println("Invalid argument to 'force-softap-channel enabled' "
699                                     + "- must be a positive integer");
700                             return -1;
701                         }
702                         int apChannel = ScanResult.convertFrequencyMhzToChannelIfSupported(
703                                 apChannelMHz);
704                         int band = ApConfigUtil.convertFrequencyToBand(apChannelMHz);
705                         int apMaxBandWidth;
706                         switch (apMaxBandWidthMHz) {
707                             case 0:
708                                 apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_AUTO;
709                                 break;
710                             case 20:
711                                 apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_20MHZ;
712                                 break;
713                             case 40:
714                                 apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_40MHZ;
715                                 break;
716                             case 80:
717                                 apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_80MHZ;
718                                 break;
719                             case 160:
720                                 apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_160MHZ;
721                                 break;
722                             case 320:
723                                 apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_320MHZ;
724                                 break;
725                             default:
726                                 pw.println("Invalid max channel bandwidth " + apMaxBandWidthMHz);
727                                 return -1;
728                         }
729                         pw.println("channel: " + apChannel + " band: " + band
730                                 + " maximum channel bandwidth: " + apMaxBandWidthMHz);
731                         if (apChannel == -1 || band == -1) {
732                             pw.println("Invalid argument to 'force-softap-channel enabled' "
733                                     + "- must be a valid WLAN channel");
734                             return -1;
735                         }
736                         boolean isTemporarilyEnablingWifiNeeded = mWifiService.getWifiEnabledState()
737                                 != WIFI_STATE_ENABLED;
738                         if (isTemporarilyEnablingWifiNeeded) {
739                             waitForWifiEnabled(true);
740                         }
741                         // Following calls will fail if wifi is not enabled
742                         boolean isValidChannel = isApChannelMHzValid(pw, apChannelMHz);
743                         if (isTemporarilyEnablingWifiNeeded) {
744                             waitForWifiEnabled(false);
745                         }
746                         if (!isValidChannel
747                                 || (band == SoftApConfiguration.BAND_5GHZ
748                                 && !mWifiService.is5GHzBandSupported())
749                                 || (band == SoftApConfiguration.BAND_6GHZ
750                                 && !mWifiService.is6GHzBandSupported())
751                                 || (band == SoftApConfiguration.BAND_60GHZ
752                                 && !mWifiService.is60GHzBandSupported())) {
753                             pw.println("Invalid argument to 'force-softap-channel enabled' "
754                                     + "- must be a valid WLAN channel"
755                                     + " in a band supported by the device");
756                             return -1;
757                         }
758                         mWifiApConfigStore.enableForceSoftApBandOrChannel(band, apChannel,
759                                 apMaxBandWidth);
760                         return 0;
761                     } else {
762                         mWifiApConfigStore.disableForceSoftApBandOrChannel();
763                         return 0;
764                     }
765                 }
766                 case "set-pno-request": {
767                     if (!SdkLevel.isAtLeastT()) {
768                         pw.println("This feature is only supported on SdkLevel T or later.");
769                         return -1;
770                     }
771                     String ssid = getNextArgRequired();
772                     int frequency = -1;
773                     WifiSsid wifiSsid = WifiSsid.fromString("\"" + ssid + "\"");
774                     String option = getNextOption();
775                     if (option != null) {
776                         if (option.equals("-f")) {
777                             frequency = Integer.parseInt(getNextArgRequired());
778                         } else {
779                             pw.println("Invalid argument to 'set-pno-request' "
780                                     + "- only allowed option is '-f'");
781                             return -1;
782                         }
783                     }
784                     int[] frequencies = frequency == -1 ? new int[0] : new int[] {frequency};
785                     IPnoScanResultsCallback.Stub callback = new IPnoScanResultsCallback.Stub() {
786                         @Override
787                         public void onScanResultsAvailable(List<ScanResult> scanResults) {
788                             Log.v(TAG, "PNO scan results available:");
789                             for (ScanResult result : scanResults) {
790                                 Log.v(TAG, result.getWifiSsid().toString());
791                             }
792                         }
793                         @Override
794                         public void onRegisterSuccess() {
795                             Log.v(TAG, "PNO scan request register success");
796                         }
797 
798                         @Override
799                         public void onRegisterFailed(int reason) {
800                             Log.v(TAG, "PNO scan request register failed reason=" + reason);
801                         }
802 
803                         @Override
804                         public void onRemoved(int reason) {
805                             Log.v(TAG, "PNO scan request callback removed reason=" + reason);
806                         }
807                     };
808                     pw.println("requesting PNO scan for: " + wifiSsid);
809                     mWifiService.setExternalPnoScanRequest(new Binder(), callback,
810                             Arrays.asList(wifiSsid), frequencies, mContext.getOpPackageName(),
811                             mContext.getAttributionTag());
812                     return 0;
813                 }
814                 case "clear-pno-request": {
815                     if (!SdkLevel.isAtLeastT()) {
816                         pw.println("This feature is only supported on SdkLevel T or later.");
817                         return -1;
818                     }
819                     mWifiService.clearExternalPnoScanRequest();
820                     return 0;
821                 }
822                 case "start-lohs": {
823                     CountDownLatch countDownLatch = new CountDownLatch(2);
824                     SoftApConfiguration config = buildSoftApConfiguration(pw);
825                     ILocalOnlyHotspotCallback.Stub lohsCallback =
826                             new ILocalOnlyHotspotCallback.Stub() {
827                         @Override
828                         public void onHotspotStarted(SoftApConfiguration config) {
829                             pw.println("Lohs onStarted, config = " + config);
830                             countDownLatch.countDown();
831                         }
832 
833                         @Override
834                         public void onHotspotStopped() {
835                             pw.println("Lohs onStopped");
836                             countDownLatch.countDown();
837                         }
838 
839                         @Override
840                         public void onHotspotFailed(int reason) {
841                             pw.println("Lohs onFailed: " + reason);
842                             countDownLatch.countDown();
843                         }
844                     };
845                     SoftApCallbackProxy softApCallback =
846                             new SoftApCallbackProxy(pw, countDownLatch);
847                     Bundle extras = new Bundle();
848                     if (SdkLevel.isAtLeastS()) {
849                         extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
850                                 mContext.getAttributionSource());
851                     }
852                     mWifiService.registerLocalOnlyHotspotSoftApCallback(softApCallback, extras);
853                     if (REQUEST_REGISTERED != mWifiService.startLocalOnlyHotspot(
854                               lohsCallback, SHELL_PACKAGE_NAME, null /* featureId */,
855                               config, extras)) {
856                         pw.println("Lohs failed to start. Please check config parameters");
857                     }
858                     // Wait for lohs to start and complete callback
859                     countDownLatch.await(10000, TimeUnit.MILLISECONDS);
860                     mWifiService.unregisterLocalOnlyHotspotSoftApCallback(softApCallback, extras);
861                     return 0;
862                 }
863                 case "start-softap": {
864                     CountDownLatch countDownLatch = new CountDownLatch(1);
865                     SoftApConfiguration config = buildSoftApConfiguration(pw);
866                     SoftApCallbackProxy softApCallback =
867                             new SoftApCallbackProxy(pw, countDownLatch);
868                     mWifiService.registerSoftApCallback(softApCallback);
869                     if (!mWifiService.startTetheredHotspot(config, SHELL_PACKAGE_NAME)) {
870                         pw.println("Soft AP failed to start. Please check config parameters");
871                     }
872                     // Wait for softap to start and complete callback
873                     countDownLatch.await(10000, TimeUnit.MILLISECONDS);
874                     mWifiService.unregisterSoftApCallback(softApCallback);
875                     return 0;
876                 }
877                 case "stop-lohs": {
878                     mWifiService.stopLocalOnlyHotspot();
879                     pw.println("Lohs stopped successfully");
880                     return 0;
881                 }
882                 case "stop-softap": {
883                     if (mWifiService.stopSoftAp()) {
884                         pw.println("Soft AP stopped successfully");
885                     } else {
886                         pw.println("Soft AP failed to stop");
887                     }
888                     return 0;
889                 }
890                 case "reload-resources": {
891                     mContext.resetResourceCache();
892                     return 0;
893                 }
894                 case "force-country-code": {
895                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
896                     if (enabled) {
897                         String countryCode = getNextArgRequired();
898                         if (!WifiCountryCode.isValid(countryCode)) {
899                             pw.println("Invalid argument: Country code must be a 2-Character"
900                                     + " alphanumeric code. But got countryCode " + countryCode
901                                     + " instead");
902                             return -1;
903                         }
904                         mWifiCountryCode.setOverrideCountryCode(countryCode);
905                         return 0;
906                     } else {
907                         mWifiCountryCode.clearOverrideCountryCode();
908                         return 0;
909                     }
910                 }
911                 case "get-country-code": {
912                     pw.println("Wifi Country Code = "
913                             + mWifiCountryCode.getCountryCode());
914                     return 0;
915                 }
916                 case "set-wifi-watchdog": {
917                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
918                     mWifiLastResortWatchdog.setWifiWatchdogFeature(enabled);
919                     return 0;
920                 }
921                 case "get-wifi-watchdog": {
922                     pw.println("wifi watchdog state is "
923                             + mWifiLastResortWatchdog.getWifiWatchdogFeature());
924                     return 0;
925                 }
926                 case "set-wifi-enabled": {
927                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
928                     mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled);
929                     return 0;
930                 }
931                 case "set-passpoint-enabled": {
932                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
933                     mWifiService.setWifiPasspointEnabled(enabled);
934                     return 0;
935                 }
936                 case "set-multi-internet-mode": {
937                     int mode = Integer.parseInt(getNextArgRequired());
938                     mWifiService.setStaConcurrencyForMultiInternetMode(mode);
939                     return 0;
940                 }
941                 case "set-scan-always-available": {
942                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
943                     mWifiService.setScanAlwaysAvailable(enabled, SHELL_PACKAGE_NAME);
944                     return 0;
945                 }
946                 case "get-softap-supported-features":
947                     // This command is used for vts to check softap supported features.
948                     if (ApConfigUtil.isAcsSupported(mContext)) {
949                         pw.println("wifi_softap_acs_supported");
950                     }
951                     if (ApConfigUtil.isWpa3SaeSupported(mContext)) {
952                         pw.println("wifi_softap_wpa3_sae_supported");
953                     }
954                     if ((mWifiService.getSupportedFeatures()
955                             & WifiManager.WIFI_FEATURE_BRIDGED_AP)
956                             == WifiManager.WIFI_FEATURE_BRIDGED_AP) {
957                         pw.println("wifi_softap_bridged_ap_supported");
958                     }
959                     if ((mWifiService.getSupportedFeatures()
960                             & WifiManager.WIFI_FEATURE_STA_BRIDGED_AP)
961                             == WifiManager.WIFI_FEATURE_STA_BRIDGED_AP) {
962                         pw.println("wifi_softap_bridged_ap_with_sta_supported");
963                     }
964                     return 0;
965                 case "settings-reset":
966                     mWifiNative.stopFakingScanDetails();
967                     mWifiNative.resetFakeScanDetails();
968                     mWifiService.factoryReset(SHELL_PACKAGE_NAME);
969                     return 0;
970                 case "list-scan-results":
971                     List<ScanResult> scanResults =
972                             mScanRequestProxy.getScanResults();
973                     if (scanResults.isEmpty()) {
974                         pw.println("No scan results");
975                     } else {
976                         ScanResultUtil.dumpScanResults(pw, scanResults,
977                                 SystemClock.elapsedRealtime());
978                     }
979                     return 0;
980                 case "start-scan":
981                     mWifiService.startScan(SHELL_PACKAGE_NAME, null);
982                     return 0;
983                 case "list-networks":
984                     ParceledListSlice<WifiConfiguration> networks =
985                             mWifiService.getConfiguredNetworks(SHELL_PACKAGE_NAME, null, false);
986                     if (networks == null || networks.getList().isEmpty()) {
987                         pw.println("No networks");
988                     } else {
989                         pw.println("Network Id      SSID                         Security type");
990                         for (WifiConfiguration network : networks.getList()) {
991                             String securityType = network.getSecurityParamsList().stream()
992                                     .map(p -> WifiConfiguration.getSecurityTypeName(
993                                                     p.getSecurityType())
994                                             + (p.isAddedByAutoUpgrade() ? "^" : ""))
995                                     .collect(Collectors.joining("/"));
996                             pw.println(String.format("%-12d %-32s %-4s",
997                                     network.networkId, WifiInfo.sanitizeSsid(network.SSID),
998                                     securityType));
999                         }
1000                     }
1001                     return 0;
1002                 case "connect-network": {
1003                     CountDownLatch countDownLatch = new CountDownLatch(1);
1004                     IActionListener.Stub actionListener = new IActionListener.Stub() {
1005                         @Override
1006                         public void onSuccess() throws RemoteException {
1007                             pw.println("Connection initiated ");
1008                             countDownLatch.countDown();
1009                         }
1010 
1011                         @Override
1012                         public void onFailure(int i) throws RemoteException {
1013                             pw.println("Connection failed");
1014                             countDownLatch.countDown();
1015                         }
1016                     };
1017                     WifiConfiguration config = buildWifiConfiguration(pw);
1018                     mWifiService.connect(config, -1, actionListener, SHELL_PACKAGE_NAME,
1019                             new Bundle());
1020                     return 0;
1021                 }
1022                 case "add-network": {
1023                     CountDownLatch countDownLatch = new CountDownLatch(1);
1024                     IActionListener.Stub actionListener = new IActionListener.Stub() {
1025                         @Override
1026                         public void onSuccess() throws RemoteException {
1027                             pw.println("Save successful");
1028                             countDownLatch.countDown();
1029                         }
1030 
1031                         @Override
1032                         public void onFailure(int i) throws RemoteException {
1033                             pw.println("Save failed");
1034                             countDownLatch.countDown();
1035                         }
1036                     };
1037                     WifiConfiguration config = buildWifiConfiguration(pw);
1038                     mWifiService.save(config, actionListener, SHELL_PACKAGE_NAME);
1039                     return 0;
1040                 }
1041                 case "forget-network": {
1042                     String networkId = getNextArgRequired();
1043                     CountDownLatch countDownLatch = new CountDownLatch(1);
1044                     IActionListener.Stub actionListener = new IActionListener.Stub() {
1045                         @Override
1046                         public void onSuccess() throws RemoteException {
1047                             pw.println("Forget successful");
1048                             countDownLatch.countDown();
1049                         }
1050 
1051                         @Override
1052                         public void onFailure(int i) throws RemoteException {
1053                             pw.println("Forget failed");
1054                             countDownLatch.countDown();
1055                         }
1056                     };
1057                     mWifiService.forget(Integer.parseInt(networkId), actionListener);
1058                     // wait for status.
1059                     countDownLatch.await(500, TimeUnit.MILLISECONDS);
1060                     return 0;
1061                 }
1062                 case "pmksa-flush": {
1063                     String networkId = getNextArgRequired();
1064                     int netId = Integer.parseInt(networkId);
1065                     WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(netId);
1066                     if (config == null) {
1067                         pw.println("No Wifi config corresponding to networkId: " + netId);
1068                         return -1;
1069                     }
1070                     mWifiNative.removeNetworkCachedData(netId);
1071                     return 0;
1072                 }
1073                 case "status":
1074                     printStatus(pw);
1075                     return 0;
1076                 case "set-verbose-logging": {
1077                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1078                     String levelOption = getNextOption();
1079                     int level = enabled ? 1 : 0;
1080                     if (enabled && levelOption != null && levelOption.equals("-l")) {
1081                         String levelStr = getNextArgRequired();
1082                         try {
1083                             level = Integer.parseInt(levelStr);
1084                             if (level < VERBOSE_LOGGING_LEVEL_DISABLED
1085                                     || level > VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY) {
1086                                 pw.println("Not a valid log level: " + level);
1087                                 return -1;
1088                             }
1089                         } catch (NumberFormatException e) {
1090                             pw.println("Invalid verbose-logging level : " + levelStr);
1091                             return -1;
1092                         }
1093                     }
1094                     mWifiService.enableVerboseLogging(level);
1095                     return 0;
1096                 }
1097                 case "is-verbose-logging": {
1098                     int enabled = mWifiService.getVerboseLoggingLevel();
1099                     pw.println(enabled > 0 ? "enabled" : "disabled");
1100                     return 0;
1101                 }
1102                 case "start-restricting-auto-join-to-subscription-id": {
1103                     if (!SdkLevel.isAtLeastS()) {
1104                         pw.println("This feature is only supported on SdkLevel S or later.");
1105                         return -1;
1106                     }
1107                     int subId = Integer.parseInt(getNextArgRequired());
1108                     mWifiService.startRestrictingAutoJoinToSubscriptionId(subId);
1109                     return 0;
1110                 }
1111                 case "stop-restricting-auto-join-to-subscription-id": {
1112                     if (!SdkLevel.isAtLeastS()) {
1113                         pw.println("This feature is only supported on SdkLevel S or later.");
1114                         return -1;
1115                     }
1116                     mWifiService.stopRestrictingAutoJoinToSubscriptionId();
1117                     return 0;
1118                 }
1119                 case "add-suggestion": {
1120                     WifiNetworkSuggestion suggestion = buildSuggestion(pw);
1121                     if (suggestion  == null) {
1122                         pw.println("Invalid network suggestion parameter");
1123                         return -1;
1124                     }
1125                     int errorCode = mWifiService.addNetworkSuggestions(
1126                             Arrays.asList(suggestion), SHELL_PACKAGE_NAME, null);
1127                     if (errorCode != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
1128                         pw.println("Add network suggestion failed with error code: " + errorCode);
1129                         return -1;
1130                     }
1131                     // untrusted/oem-paid networks need a corresponding NetworkRequest.
1132                     if (suggestion.isUntrusted()
1133                             || (SdkLevel.isAtLeastS()
1134                             && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
1135                         NetworkRequest.Builder networkRequestBuilder =
1136                                 new NetworkRequest.Builder()
1137                                         .addTransportType(TRANSPORT_WIFI);
1138                         if (suggestion.isUntrusted()) {
1139                             networkRequestBuilder.removeCapability(NET_CAPABILITY_TRUSTED);
1140                         }
1141                         if (SdkLevel.isAtLeastS()) {
1142                             if (suggestion.isOemPaid()) {
1143                                 networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PAID);
1144                             }
1145                             if (suggestion.isOemPrivate()) {
1146                                 networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PRIVATE);
1147                             }
1148                         }
1149                         NetworkRequest networkRequest = networkRequestBuilder.build();
1150                         ConnectivityManager.NetworkCallback networkCallback =
1151                                 new ConnectivityManager.NetworkCallback();
1152                         pw.println("Adding request: " + networkRequest);
1153                         mConnectivityManager.requestNetwork(networkRequest, networkCallback);
1154                         sActiveRequests.put(
1155                                 suggestion.getSsid(), Pair.create(networkRequest, networkCallback));
1156                     }
1157                     return 0;
1158                 }
1159                 case "remove-suggestion": {
1160                     String ssid = getNextArgRequired();
1161                     String action = getNextArg();
1162                     int actionCode = ACTION_REMOVE_SUGGESTION_DISCONNECT;
1163                     if (action != null && action.equals("lingering")) {
1164                         actionCode = ACTION_REMOVE_SUGGESTION_LINGER;
1165                     }
1166                     List<WifiNetworkSuggestion> suggestions =
1167                             mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
1168                     WifiNetworkSuggestion suggestion = suggestions.stream()
1169                             .filter(s -> s.getSsid().equals(ssid))
1170                             .findAny()
1171                             .orElse(null);
1172                     if (suggestion == null) {
1173                         pw.println("No matching suggestion to remove");
1174                         return -1;
1175                     }
1176                     mWifiService.removeNetworkSuggestions(
1177                             Arrays.asList(suggestion), SHELL_PACKAGE_NAME, actionCode);
1178                     // untrusted/oem-paid networks need a corresponding NetworkRequest.
1179                     if (suggestion.isUntrusted()
1180                             || (SdkLevel.isAtLeastS()
1181                             && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
1182                         Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
1183                                 sActiveRequests.remove(suggestion.getSsid());
1184                         if (nrAndNc == null) {
1185                             pw.println("No matching request to remove");
1186                             return -1;
1187                         }
1188                         pw.println("Removing request: " + nrAndNc.first);
1189                         mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
1190                     }
1191                     return 0;
1192                 }
1193                 case "remove-all-suggestions":
1194                     mWifiService.removeNetworkSuggestions(
1195                             Collections.emptyList(), SHELL_PACKAGE_NAME,
1196                             WifiManager.ACTION_REMOVE_SUGGESTION_DISCONNECT);
1197                     return 0;
1198                 case "list-suggestions": {
1199                     List<WifiNetworkSuggestion> suggestions =
1200                             mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
1201                     printWifiNetworkSuggestions(pw, suggestions);
1202                     return 0;
1203                 }
1204                 case "list-all-suggestions": {
1205                     Set<WifiNetworkSuggestion> suggestions =
1206                             mWifiNetworkSuggestionsManager.getAllNetworkSuggestions();
1207                     printWifiNetworkSuggestions(pw, suggestions);
1208                     return 0;
1209                 }
1210                 case "list-suggestions-from-app": {
1211                     String packageName = getNextArgRequired();
1212                     List<WifiNetworkSuggestion> suggestions =
1213                             mWifiService.getNetworkSuggestions(packageName);
1214                     printWifiNetworkSuggestions(pw, suggestions);
1215                     return 0;
1216                 }
1217                 case "allow-root-to-get-local-only-cmm": {
1218                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1219                     mActiveModeWarden.allowRootToGetLocalOnlyCmm(enabled);
1220                     return 0;
1221                 }
1222                 case "add-request": {
1223                     Pair<String, NetworkRequest> result = buildNetworkRequest(pw);
1224                     String ssid = result.first;
1225                     NetworkRequest networkRequest = result.second;
1226                     ConnectivityManager.NetworkCallback networkCallback =
1227                             new ConnectivityManager.NetworkCallback();
1228                     pw.println("Adding request: " + networkRequest);
1229                     mWifiThreadRunner.post(() -> mConnectivityManager
1230                                     .requestNetwork(networkRequest, networkCallback),
1231                             "shell#add-request");
1232 
1233                     sActiveRequests.put(ssid, Pair.create(networkRequest, networkCallback));
1234                     return 0;
1235                 }
1236                 case "remove-request": {
1237                     String ssid = getNextArgRequired();
1238                     Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
1239                             sActiveRequests.remove(ssid);
1240                     if (nrAndNc == null) {
1241                         pw.println("No matching request to remove");
1242                         return -1;
1243                     }
1244                     pw.println("Removing request: " + nrAndNc.first);
1245                     mWifiThreadRunner.post(() -> mConnectivityManager
1246                                     .unregisterNetworkCallback(nrAndNc.second),
1247                             "shell#remove-request");
1248                     return 0;
1249                 }
1250                 case "remove-all-requests":
1251                     if (sActiveRequests.isEmpty()) {
1252                         pw.println("No active requests");
1253                         return -1;
1254                     }
1255                     for (Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc
1256                             : sActiveRequests.values()) {
1257                         pw.println("Removing request: " + nrAndNc.first);
1258                         mWifiThreadRunner.post(() ->
1259                                 mConnectivityManager.unregisterNetworkCallback(nrAndNc.second),
1260                                 "shell#remove-request");
1261                     }
1262                     sActiveRequests.clear();
1263                     return 0;
1264                 case "list-requests":
1265                     if (sActiveRequests.isEmpty()) {
1266                         pw.println("No active requests");
1267                     } else {
1268                         pw.println("SSID                         NetworkRequest");
1269                         for (Map.Entry<String,
1270                                 Pair<NetworkRequest, ConnectivityManager.NetworkCallback>> entry :
1271                                 sActiveRequests.entrySet()) {
1272                             pw.println(String.format("%-32s %-4s",
1273                                     entry.getKey(), entry.getValue().first));
1274                         }
1275                     }
1276                     return 0;
1277                 case "network-requests-set-user-approved": {
1278                     String packageName = getNextArgRequired();
1279                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
1280                     mWifiNetworkFactory.setUserApprovedApp(packageName, approved);
1281                     return 0;
1282                 }
1283                 case "network-requests-has-user-approved": {
1284                     String packageName = getNextArgRequired();
1285                     boolean hasUserApproved = mWifiNetworkFactory.hasUserApprovedApp(packageName);
1286                     pw.println(hasUserApproved ? "yes" : "no");
1287                     return 0;
1288                 }
1289                 case "set-coex-cell-channels": {
1290                     if (!SdkLevel.isAtLeastS()) {
1291                         return handleDefaultCommands(cmd);
1292                     }
1293                     mCoexManager.setMockCellChannels(buildCoexCellChannels());
1294                     return 0;
1295                 }
1296                 case "reset-coex-cell-channels": {
1297                     if (!SdkLevel.isAtLeastS()) {
1298                         return handleDefaultCommands(cmd);
1299                     }
1300                     mCoexManager.resetMockCellChannels();
1301                     return 0;
1302                 }
1303                 case "get-coex-cell-channels": {
1304                     if (!SdkLevel.isAtLeastS()) {
1305                         return handleDefaultCommands(cmd);
1306                     }
1307                     pw.println("Cell channels: " + mCoexManager.getCellChannels());
1308                     return 0;
1309                 }
1310                 case "set-connected-score": {
1311                     int score = Integer.parseInt(getNextArgRequired());
1312                     CountDownLatch countDownLatch = new CountDownLatch(2);
1313                     mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer
1314                     WifiScorer connectedScorer = new WifiScorer(mWifiService, countDownLatch);
1315                     if (mWifiService.setWifiConnectedNetworkScorer(new Binder(), connectedScorer)) {
1316                         // wait for retrieving the session id & score observer.
1317                         countDownLatch.await(1000, TimeUnit.MILLISECONDS);
1318                     }
1319                     if (connectedScorer.getSessionId() == null
1320                             || connectedScorer.getScoreUpdateObserver() == null) {
1321                         pw.println("Did not receive session id and/or the score update observer. "
1322                                 + "Is the device connected to a wifi network?");
1323                         mWifiService.clearWifiConnectedNetworkScorer();
1324                         return -1;
1325                     }
1326                     pw.println("Updating score: " + score + " for session id: "
1327                             + connectedScorer.getSessionId());
1328                     try {
1329                         connectedScorer.getScoreUpdateObserver().notifyScoreUpdate(
1330                                 connectedScorer.getSessionId(), score);
1331                     } catch (RemoteException e) {
1332                         pw.println("Failed to send the score update");
1333                         mWifiService.clearWifiConnectedNetworkScorer();
1334                         return -1;
1335                     }
1336                     return 0;
1337                 }
1338                 case "reset-connected-score": {
1339                     mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer
1340                     return 0;
1341                 }
1342                 case "network-suggestions-set-as-carrier-provider": {
1343                     String packageName = getNextArgRequired();
1344                     boolean enabled = getNextArgRequiredTrueOrFalse("yes", "no");
1345                     mWifiNetworkSuggestionsManager
1346                             .setAppWorkingAsCrossCarrierProvider(packageName, enabled);
1347                     return 0;
1348                 }
1349                 case "is-network-suggestions-set-as-carrier-provider": {
1350                     String packageName = getNextArgRequired();
1351                     pw.println(mWifiNetworkSuggestionsManager
1352                             .isAppWorkingAsCrossCarrierProvider(packageName) ? "yes" : "no");
1353                     return 0;
1354                 }
1355                 case "remove-shell-app-from-suggestion_database <packageName>": {
1356                     String packageName = getNextArgRequired();
1357                     mWifiNetworkSuggestionsManager.removeApp(packageName);
1358                     return 0;
1359                 }
1360                 case "set-emergency-callback-mode": {
1361                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1362                     mActiveModeWarden.emergencyCallbackModeChanged(enabled);
1363                     return 0;
1364                 }
1365                 case "set-emergency-call-state": {
1366                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1367                     mActiveModeWarden.emergencyCallStateChanged(enabled);
1368                     return 0;
1369                 }
1370                 case "set-emergency-scan-request": {
1371                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1372                     mWifiService.setEmergencyScanRequestInProgress(enabled);
1373                     return 0;
1374                 }
1375                 case "trigger-recovery": {
1376                     mSelfRecovery.trigger(REASON_API_CALL);
1377                     return 0;
1378                 }
1379                 case "add-fake-scan": {
1380                     String option = getNextOption();
1381                     boolean isHex = (option != null && option.equals("-x"));
1382                     WifiSsid wifiSsid = WifiSsid.fromBytes(isHex
1383                             ? HexEncoding.decode(getNextArgRequired())
1384                             : getNextArgRequired().getBytes(StandardCharsets.UTF_8));
1385                     String bssid = getNextArgRequired();
1386                     String capabilities = getNextArgRequired();
1387                     int frequency;
1388                     int dbm;
1389                     String freqStr = getNextArgRequired();
1390                     try {
1391                         frequency = Integer.parseInt(freqStr);
1392                     } catch (NumberFormatException e) {
1393                         pw.println(
1394                                 "Invalid frequency argument to 'add-fake-scan' "
1395                                         + "- must be an integer: " + freqStr);
1396                         return -1;
1397                     }
1398                     if (frequency <= 0) {
1399                         pw.println("Invalid frequency argument to 'add-fake-scan' - must be a "
1400                                 + "positive integer: " + freqStr);
1401                     }
1402                     String dbmString = getNextArgRequired();
1403                     try {
1404                         dbm = Integer.parseInt(dbmString);
1405                     } catch (NumberFormatException e) {
1406                         pw.println(
1407                                 "Invalid dbm argument to 'add-fake-scan' "
1408                                         + "- must be an integer: " + dbmString);
1409                         return -1;
1410                     }
1411                     ScanResult.InformationElement ieSSid = new ScanResult.InformationElement(
1412                             ScanResult.InformationElement.EID_SSID,
1413                             0,
1414                             wifiSsid.getBytes());
1415                     ScanResult.InformationElement[] ies =
1416                             new ScanResult.InformationElement[]{ieSSid};
1417                     ScanDetail sd = new ScanDetail(new NetworkDetail(bssid, ies, null, frequency),
1418                             wifiSsid, bssid, capabilities, dbm,
1419                             frequency, SystemClock.elapsedRealtime() * 1000, ies, null, null);
1420                     mWifiNative.addFakeScanDetail(sd);
1421                     return 0;
1422                 }
1423                 case "reset-fake-scans":
1424                     mWifiNative.resetFakeScanDetails();
1425                     return 0;
1426                 case "start-faking-scans":
1427                     mWifiNative.startFakingScanDetails();
1428                     mWifiService.startScan(SHELL_PACKAGE_NAME, null); // to trigger update
1429                     return 0;
1430                 case "stop-faking-scans":
1431                     mWifiNative.stopFakingScanDetails();
1432                     return 0;
1433                 case "enable-scanning": {
1434                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
1435                     boolean hiddenEnabled = false;
1436                     String option = getNextOption();
1437                     if (option != null) {
1438                         if (option.equals("-h")) {
1439                             hiddenEnabled = true;
1440                         } else {
1441                             pw.println("Invalid argument to 'enable-scanning' "
1442                                     + "- only allowed option is '-h'");
1443                             return -1;
1444                         }
1445                     }
1446                     mScanRequestProxy.enableScanning(enabled, hiddenEnabled);
1447                     return 0;
1448                 }
1449                 case "launch-dialog-simple":
1450                     String title = null;
1451                     String message = null;
1452                     String messageUrl = null;
1453                     int messageUrlStart = 0;
1454                     int messageUrlEnd = 0;
1455                     String positiveButtonText = null;
1456                     String negativeButtonText = null;
1457                     String neutralButtonText = null;
1458                     String dialogOption = getNextOption();
1459                     boolean simpleTimeoutSpecified = false;
1460                     long simpleTimeoutMs = 0;
1461                     boolean useLegacy = false;
1462                     while (dialogOption != null) {
1463                         switch (dialogOption) {
1464                             case "-t":
1465                                 title = getNextArgRequired();
1466                                 break;
1467                             case "-m":
1468                                 message = getNextArgRequired();
1469                                 break;
1470                             case "-l":
1471                                 messageUrl = getNextArgRequired();
1472                                 messageUrlStart = Integer.valueOf(getNextArgRequired());
1473                                 messageUrlEnd = Integer.valueOf(getNextArgRequired());
1474                                 break;
1475                             case "-y":
1476                                 positiveButtonText = getNextArgRequired();
1477                                 break;
1478                             case "-n":
1479                                 negativeButtonText = getNextArgRequired();
1480                                 break;
1481                             case "-x":
1482                                 neutralButtonText = getNextArgRequired();
1483                                 break;
1484                             case "-c":
1485                                 simpleTimeoutMs = Integer.parseInt(getNextArgRequired());
1486                                 simpleTimeoutSpecified = true;
1487                                 break;
1488                             case "-s":
1489                                 useLegacy = true;
1490                                 break;
1491                             default:
1492                                 pw.println("Ignoring unknown option " + dialogOption);
1493                                 break;
1494                         }
1495                         dialogOption = getNextOption();
1496                     }
1497                     ArrayBlockingQueue<String> simpleQueue = new ArrayBlockingQueue<>(1);
1498                     WifiDialogManager.SimpleDialogCallback wifiEnableRequestCallback =
1499                             new WifiDialogManager.SimpleDialogCallback() {
1500                                 @Override
1501                                 public void onPositiveButtonClicked() {
1502                                     simpleQueue.offer("Positive button was clicked.");
1503                                 }
1504 
1505                                 @Override
1506                                 public void onNegativeButtonClicked() {
1507                                     simpleQueue.offer("Negative button was clicked.");
1508                                 }
1509 
1510                                 @Override
1511                                 public void onNeutralButtonClicked() {
1512                                     simpleQueue.offer("Neutral button was clicked.");
1513                                 }
1514 
1515                                 @Override
1516                                 public void onCancelled() {
1517                                     simpleQueue.offer("Dialog was cancelled.");
1518                                 }
1519                             };
1520                     WifiDialogManager.DialogHandle simpleDialogHandle;
1521                     if (useLegacy) {
1522                         simpleDialogHandle = mWifiDialogManager.createLegacySimpleDialogWithUrl(
1523                                 title,
1524                                 message,
1525                                 messageUrl,
1526                                 messageUrlStart,
1527                                 messageUrlEnd,
1528                                 positiveButtonText,
1529                                 negativeButtonText,
1530                                 neutralButtonText,
1531                                 wifiEnableRequestCallback,
1532                                 mWifiThreadRunner);
1533                     } else {
1534                         simpleDialogHandle = mWifiDialogManager.createSimpleDialogWithUrl(
1535                                 title,
1536                                 message,
1537                                 messageUrl,
1538                                 messageUrlStart,
1539                                 messageUrlEnd,
1540                                 positiveButtonText,
1541                                 negativeButtonText,
1542                                 neutralButtonText,
1543                                 wifiEnableRequestCallback,
1544                                 mWifiThreadRunner);
1545                     }
1546                     if (simpleTimeoutSpecified) {
1547                         simpleDialogHandle.launchDialog(simpleTimeoutMs);
1548                         pw.println("Launched dialog with " + simpleTimeoutMs + " millisecond"
1549                                 + " timeout. Waiting for user response...");
1550                         pw.flush();
1551                         String dialogResponse = simpleQueue.take();
1552                         if (dialogResponse == null) {
1553                             pw.println("No response received.");
1554                         } else {
1555                             pw.println(dialogResponse);
1556                         }
1557                     } else {
1558                         simpleDialogHandle.launchDialog();
1559                         pw.println("Launched dialog. Waiting up to 15 seconds for user response"
1560                                 + " before dismissing...");
1561                         pw.flush();
1562                         String dialogResponse = simpleQueue.poll(15, TimeUnit.SECONDS);
1563                         if (dialogResponse == null) {
1564                             pw.println("No response received. Dismissing dialog.");
1565                             simpleDialogHandle.dismissDialog();
1566                         } else {
1567                             pw.println(dialogResponse);
1568                         }
1569                     }
1570                     return 0;
1571                 case "launch-dialog-p2p-invitation-sent": {
1572                     int displayId = Display.DEFAULT_DISPLAY;
1573                     String deviceName = getNextArgRequired();
1574                     String displayPin = getNextArgRequired();
1575                     String cmdOption = getNextOption();
1576                     if (cmdOption != null && cmdOption.equals("-i")) {
1577                         String displayIdStr = getNextArgRequired();
1578                         try {
1579                             displayId = Integer.parseInt(displayIdStr);
1580                         } catch (NumberFormatException e) {
1581                             pw.println("Invalid <display-id> argument to "
1582                                     + "'launch-dialog-p2p-invitation-sent' "
1583                                     + "- must be an integer: "
1584                                     + displayIdStr);
1585                             return -1;
1586                         }
1587                         DisplayManager dm = mContext.getSystemService(DisplayManager.class);
1588                         Display[] displays = dm.getDisplays();
1589                         for (Display display : displays) {
1590                             pw.println("Display: id=" + display.getDisplayId() + ", info="
1591                                     + display.getDeviceProductInfo());
1592                         }
1593                     }
1594                     mWifiDialogManager.createP2pInvitationSentDialog(deviceName, displayPin,
1595                             displayId).launchDialog();
1596                     pw.println("Launched dialog.");
1597                     return 0;
1598                 }
1599                 case "launch-dialog-p2p-invitation-received": {
1600                     String deviceName = getNextArgRequired();
1601                     boolean isPinRequested = false;
1602                     String displayPin = null;
1603                     String pinOption = getNextOption();
1604                     int displayId = Display.DEFAULT_DISPLAY;
1605                     boolean p2pInvRecTimeoutSpecified = false;
1606                     long p2pInvRecTimeout = 0;
1607                     while (pinOption != null) {
1608                         if (pinOption.equals("-p")) {
1609                             isPinRequested = true;
1610                         } else if (pinOption.equals("-d")) {
1611                             displayPin = getNextArgRequired();
1612                         } else if (pinOption.equals("-i")) {
1613                             String displayIdStr = getNextArgRequired();
1614                             try {
1615                                 displayId = Integer.parseInt(displayIdStr);
1616                             } catch (NumberFormatException e) {
1617                                 pw.println("Invalid <display-id> argument to "
1618                                         + "'launch-dialog-p2p-invitation-received' "
1619                                         + "- must be an integer: "
1620                                         + displayIdStr);
1621                                 return -1;
1622                             }
1623                             DisplayManager dm = mContext.getSystemService(DisplayManager.class);
1624                             Display[] displays = dm.getDisplays();
1625                             for (Display display : displays) {
1626                                 pw.println("Display: id=" + display.getDisplayId() + ", info="
1627                                         + display.getDeviceProductInfo());
1628                             }
1629                         } else if (pinOption.equals("-c")) {
1630                             p2pInvRecTimeout = Integer.parseInt(getNextArgRequired());
1631                             p2pInvRecTimeoutSpecified = true;
1632                         } else {
1633                             pw.println("Ignoring unknown option " + pinOption);
1634                         }
1635                         pinOption = getNextOption();
1636                     }
1637                     ArrayBlockingQueue<String> p2pInvRecQueue = new ArrayBlockingQueue<>(1);
1638                     WifiDialogManager.P2pInvitationReceivedDialogCallback callback =
1639                             new WifiDialogManager.P2pInvitationReceivedDialogCallback() {
1640                         @Override
1641                         public void onAccepted(@Nullable String optionalPin) {
1642                             p2pInvRecQueue.offer("Invitation accepted with optionalPin="
1643                                     + optionalPin);
1644                         }
1645 
1646                         @Override
1647                         public void onDeclined() {
1648                             p2pInvRecQueue.offer("Invitation declined");
1649                         }
1650                     };
1651                     WifiDialogManager.DialogHandle p2pInvitationReceivedDialogHandle =
1652                             mWifiDialogManager.createP2pInvitationReceivedDialog(
1653                                     deviceName,
1654                                     isPinRequested,
1655                                     displayPin,
1656                                     displayId,
1657                                     callback,
1658                                     mWifiThreadRunner);
1659                     if (p2pInvRecTimeoutSpecified) {
1660                         p2pInvitationReceivedDialogHandle.launchDialog(p2pInvRecTimeout);
1661                         pw.println("Launched dialog with " + p2pInvRecTimeout + " millisecond"
1662                                 + " timeout. Waiting for user response...");
1663                         pw.flush();
1664                         String dialogResponse = p2pInvRecQueue.take();
1665                         if (dialogResponse == null) {
1666                             pw.println("No response received.");
1667                         } else {
1668                             pw.println(dialogResponse);
1669                         }
1670                     } else {
1671                         p2pInvitationReceivedDialogHandle.launchDialog();
1672                         pw.println("Launched dialog. Waiting up to 15 seconds for user response"
1673                                 + " before dismissing...");
1674                         pw.flush();
1675                         String dialogResponse = p2pInvRecQueue.poll(15, TimeUnit.SECONDS);
1676                         if (dialogResponse == null) {
1677                             pw.println("No response received. Dismissing dialog.");
1678                             p2pInvitationReceivedDialogHandle.dismissDialog();
1679                         } else {
1680                             pw.println(dialogResponse);
1681                         }
1682                     }
1683                     return 0;
1684                 }
1685                 case "query-interface": {
1686                     String uidArg = getNextArgRequired();
1687                     int uid = 0;
1688                     try {
1689                         uid = Integer.parseInt(uidArg);
1690                     } catch (NumberFormatException e) {
1691                         pw.println(
1692                                 "Invalid UID specified, can't convert to an integer - " + uidArg);
1693                         return -1;
1694                     }
1695                     String packageName = getNextArgRequired();
1696 
1697                     String interfaceTypeArg = getNextArgRequired();
1698                     int interfaceType;
1699                     switch (interfaceTypeArg) {
1700                         case "STA":
1701                             interfaceType = HDM_CREATE_IFACE_STA;
1702                             break;
1703                         case "AP":
1704                             interfaceType = HDM_CREATE_IFACE_AP;
1705                             break;
1706                         case "AWARE":
1707                             interfaceType = HDM_CREATE_IFACE_NAN;
1708                             break;
1709                         case "DIRECT":
1710                             interfaceType = HDM_CREATE_IFACE_P2P;
1711                             break;
1712                         default:
1713                             pw.println("Invalid interface type - expected STA|AP|AWARE|DIRECT: "
1714                                     + interfaceTypeArg);
1715                             return -1;
1716                     }
1717                     boolean queryForNewInterface = false;
1718                     String optArg = getNextArg();
1719                     if (optArg != null) {
1720                         if (TextUtils.equals("-new", optArg)) {
1721                             queryForNewInterface = true;
1722                         } else {
1723                             pw.println("Unknown extra arg --- " + optArg);
1724                             return -1;
1725                         }
1726                     }
1727                     List<Pair<Integer, WorkSource>> details =
1728                             mHalDeviceManager.reportImpactToCreateIface(interfaceType,
1729                                     queryForNewInterface, new WorkSource(uid, packageName));
1730                     final SparseArray<String> ifaceMap = new SparseArray<String>() {{
1731                             put(HDM_CREATE_IFACE_STA, "STA");
1732                             put(HDM_CREATE_IFACE_AP, "AP");
1733                             put(HDM_CREATE_IFACE_AP_BRIDGE, "AP");
1734                             put(HDM_CREATE_IFACE_P2P, "DIRECT");
1735                             put(HDM_CREATE_IFACE_NAN, "AWARE");
1736                         }};
1737                     if (details == null) {
1738                         pw.println("Can't create interface: " + interfaceTypeArg);
1739                     } else if (details.size() == 0) {
1740                         pw.println("Interface " + interfaceTypeArg
1741                                 + " can be created without destroying any other interfaces");
1742                     } else {
1743                         pw.println("Interface " + interfaceTypeArg
1744                                 + " can be created. Following interfaces will be destroyed:");
1745                         for (Pair<Integer, WorkSource> detail : details) {
1746                             pw.println("    Type=" + ifaceMap.get(detail.first) + ", WS="
1747                                     + detail.second);
1748                         }
1749                     }
1750                     return 0;
1751                 }
1752                 case "interface-priority-interactive-mode": {
1753                     String flag = getNextArgRequired(); // enable|disable|default
1754                     switch (flag) {
1755                         case "enable":
1756                             mInterfaceConflictManager.setUserApprovalNeededOverride(true, true);
1757                             break;
1758                         case "disable":
1759                             mInterfaceConflictManager.setUserApprovalNeededOverride(true, false);
1760                             break;
1761                         case "default":
1762                             mInterfaceConflictManager.setUserApprovalNeededOverride(
1763                                     false, /* don't care */ false);
1764                             break;
1765                         default:
1766                             pw.println(
1767                                     "Invalid argument to `interface-priority-interactive-mode` - "
1768                                             + flag);
1769                             return -1;
1770                     }
1771                     return 0;
1772                 }
1773                 case "set-one-shot-screen-on-delay-ms": {
1774                     if (!SdkLevel.isAtLeastT()) {
1775                         pw.println("This feature is only supported on SdkLevel T or later.");
1776                         return -1;
1777                     }
1778                     int delay = Integer.parseInt(getNextArgRequired());
1779                     mWifiService.setOneShotScreenOnConnectivityScanDelayMillis(delay);
1780                     return 0;
1781                 }
1782                 case "set-network-selection-config": {
1783                     if (!SdkLevel.isAtLeastT()) {
1784                         pw.println("This feature is only supported on SdkLevel T or later.");
1785                         return -1;
1786                     }
1787                     WifiNetworkSelectionConfig.Builder builder =
1788                             new WifiNetworkSelectionConfig.Builder();
1789                     builder.setSufficiencyCheckEnabledWhenScreenOff(getNextArgRequiredTrueOrFalse(
1790                             "enabled", "disabled"));
1791                     builder.setSufficiencyCheckEnabledWhenScreenOn(getNextArgRequiredTrueOrFalse(
1792                             "enabled", "disabled"));
1793 
1794                     String option = getNextOption();
1795                     while (option != null) {
1796                         if (option.equals("-a")) {
1797                             String associatedNetworkSelectionOverride = getNextArgRequired();
1798                             int override = Integer.parseInt(associatedNetworkSelectionOverride);
1799                             builder.setAssociatedNetworkSelectionOverride(override);
1800                         } else {
1801                             pw.println("Ignoring unknown option " + option);
1802                         }
1803                         option = getNextOption();
1804                     }
1805                     WifiNetworkSelectionConfig nsConfig;
1806                     try {
1807                         nsConfig = builder.build();
1808                     } catch (Exception e) {
1809                         pw.println("Failed to build wifi network selection config.");
1810                         return -1;
1811                     }
1812                     mWifiService.setNetworkSelectionConfig(nsConfig);
1813                     return 0;
1814                 }
1815                 case "start-dpp-enrollee-responder": {
1816                     CountDownLatch countDownLatch = new CountDownLatch(1);
1817                     String option = getNextOption();
1818                     String info = null;
1819                     int curve = 0;
1820                     while (option != null) {
1821                         if (option.equals("-i")) {
1822                             info = getNextArgRequired();
1823                         } else if (option.equals("-c")) {
1824                             curve = Integer.parseInt(getNextArgRequired());
1825                         } else {
1826                             pw.println("Ignoring unknown option " + option);
1827                         }
1828                         option = getNextOption();
1829                     }
1830                     mWifiService.startDppAsEnrolleeResponder(new Binder(), info, curve,
1831                             new DppCallbackProxy(pw, countDownLatch));
1832                     // Wait for DPP callback
1833                     countDownLatch.await(10000, TimeUnit.MILLISECONDS);
1834                     return 0;
1835                 }
1836                 case "start-dpp-configurator-initiator": {
1837                     CountDownLatch countDownLatch = new CountDownLatch(1);
1838                     int netId = Integer.parseInt(getNextArgRequired());
1839                     int role = Integer.parseInt(getNextArgRequired());
1840                     String enrolleeUri = getNextArgRequired();
1841                     mWifiService.startDppAsConfiguratorInitiator(new Binder(), SHELL_PACKAGE_NAME,
1842                             enrolleeUri, netId, role, new DppCallbackProxy(pw, countDownLatch));
1843                     // Wait for DPP callback
1844                     countDownLatch.await(10000, TimeUnit.MILLISECONDS);
1845                     return 0;
1846                 }
1847                 case "stop-dpp":
1848                     mWifiService.stopDppSession();
1849                     return 0;
1850                 case "set-ssid-charset":
1851                     String lang = getNextArgRequired();
1852                     Charset charset = Charset.forName(getNextArgRequired());
1853                     mSsidTranslator.setMockLocaleCharset(lang, charset);
1854                     return 0;
1855                 case "clear-ssid-charsets":
1856                     mSsidTranslator.clearMockLocaleCharsets();
1857                     return 0;
1858                 case "take-bugreport": {
1859                     if (mDeviceConfig.isInterfaceFailureBugreportEnabled()) {
1860                         mWifiDiagnostics.takeBugReport("Wifi bugreport test", "");
1861                     }
1862                     return 0;
1863                 }
1864                 case "get-allowed-channel": {
1865                     StringBuilder allowedChannel = new StringBuilder();
1866                     int band = WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ;
1867 
1868                     String option = getNextOption();
1869                     while (option != null) {
1870                         if (option.equals("-b")) {
1871                             band = Integer.parseInt(getNextArgRequired());
1872                         } else {
1873                             pw.println("Ignoring unknown option: " + option);
1874                             return -1;
1875                         }
1876                         option = getNextOption();
1877                     }
1878 
1879                     try {
1880                         Bundle extras = new Bundle();
1881                         if (SdkLevel.isAtLeastS()) {
1882                             extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
1883                                     mContext.getAttributionSource());
1884                         } else {
1885                             throw new UnsupportedOperationException();
1886                         }
1887                         // The option "-b 2" (getting band 5ghz active channels) is valid for old
1888                         // devices, but invalid for new devices. So we first check if device
1889                         // supports getUsableChannels() API.
1890                         mWifiService.getUsableChannels(WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ,
1891                                 WifiAvailableChannel.OP_MODE_STA,
1892                                 WifiAvailableChannel.FILTER_REGULATORY, SHELL_PACKAGE_NAME, extras);
1893                         try {
1894                             for (int opMode : OP_MODE_LIST) {
1895                                 List<WifiAvailableChannel> usableChannels =
1896                                         mWifiService.getUsableChannels(band, opMode,
1897                                                 WifiAvailableChannel.FILTER_REGULATORY,
1898                                                 SHELL_PACKAGE_NAME, extras);
1899                                 allowedChannel = new StringBuilder();
1900                                 for (WifiAvailableChannel channel : usableChannels) {
1901                                     allowedChannel.append(channel.getFrequencyMhz()).append(" ");
1902                                 }
1903                                 pw.println("Allowed ch in " + getOpModeName(opMode) + " mode:\n"
1904                                         + allowedChannel);
1905                             }
1906                         } catch (IllegalArgumentException e) {
1907                             pw.println("Invalid band: " + band);
1908                             return -1;
1909                         }
1910                     } catch (UnsupportedOperationException e) {
1911                         WifiScanner wifiScanner = mContext.getSystemService(WifiScanner.class);
1912                         try {
1913                             List<Integer> availableChannels = wifiScanner.getAvailableChannels(
1914                                     band);
1915                             for (Integer channel : availableChannels) {
1916                                 allowedChannel.append(channel).append(" ");
1917                             }
1918                             pw.println("Allowed ch in all modes:\n" + allowedChannel);
1919                         } catch (SecurityException securityException) {
1920                             pw.println("Permission is required.");
1921                             return -1;
1922                         }
1923                     } catch (SecurityException e) {
1924                         pw.println("Permission is required.");
1925                         return -1;
1926                     }
1927                     return 0;
1928                 }
1929                 case "trigger-afc-location-update":
1930                     Double longitude = Double.parseDouble(getNextArgRequired());
1931                     Double latitude = Double.parseDouble(getNextArgRequired());
1932                     Double height = Double.parseDouble(getNextArgRequired());
1933                     Location location = new Location(LocationManager.FUSED_PROVIDER);
1934                     location.setLongitude(longitude);
1935                     location.setLatitude(latitude);
1936                     location.setAltitude(height);
1937                     mWifiThreadRunner.post(() -> mAfcManager.onLocationChange(location, true),
1938                             TAG + "#" + cmd);
1939                     pw.println("The updated location with longitude of " + longitude + " degrees, "
1940                             + "latitude of " + latitude + " degrees, and height of " + height
1941                             + " meters was passed into the Afc Manager onLocationChange method.");
1942                     return 0;
1943                 case "set-afc-channel-allowance": {
1944                     WifiChip.AfcChannelAllowance afcChannelAllowance =
1945                             new WifiChip.AfcChannelAllowance();
1946                     boolean expiryTimeArgumentIncluded = false;
1947                     boolean frequencyOrChannelArgumentIncluded = false;
1948                     afcChannelAllowance.availableAfcFrequencyInfos = new ArrayList<>();
1949                     afcChannelAllowance.availableAfcChannelInfos = new ArrayList<>();
1950 
1951                     String option;
1952                     while ((option = getNextOption()) != null) {
1953                         switch (option) {
1954                             case "-e": {
1955                                 int secondsUntilExpiry = Integer.parseInt(getNextArgRequired());
1956                                 // AfcChannelAllowance requires this field to be a UNIX timestamp
1957                                 // in milliseconds.
1958                                 afcChannelAllowance.availabilityExpireTimeMs =
1959                                         System.currentTimeMillis() + secondsUntilExpiry * 1000;
1960                                 expiryTimeArgumentIncluded = true;
1961 
1962                                 break;
1963                             }
1964                             case "-f": {
1965                                 frequencyOrChannelArgumentIncluded = true;
1966                                 String frequenciesInput = getNextArgRequired();
1967 
1968                                 if (frequenciesInput.equals(("none"))) {
1969                                     break;
1970                                 }
1971 
1972                                 // parse frequency list, and add it to the AfcChannelAllowance
1973                                 String[] unparsedFrequencies = frequenciesInput.split(":");
1974                                 afcChannelAllowance.availableAfcFrequencyInfos = new ArrayList<>();
1975 
1976                                 for (int i = 0; i < unparsedFrequencies.length; ++i) {
1977                                     String[] frequencyPieces = unparsedFrequencies[i].split(",");
1978 
1979                                     if (frequencyPieces.length != 3) {
1980                                         throw new IllegalArgumentException("Each frequency in the "
1981                                                 + "available frequency list should have 3 values, "
1982                                                 + "but found one with " + frequencyPieces.length);
1983                                     }
1984 
1985                                     WifiChip.AvailableAfcFrequencyInfo frequencyInfo = new
1986                                             WifiChip.AvailableAfcFrequencyInfo();
1987 
1988                                     frequencyInfo.startFrequencyMhz =
1989                                             Integer.parseInt(frequencyPieces[0]);
1990                                     frequencyInfo.endFrequencyMhz =
1991                                             Integer.parseInt(frequencyPieces[1]);
1992                                     frequencyInfo.maxPsdDbmPerMhz =
1993                                             Integer.parseInt(frequencyPieces[2]);
1994 
1995                                     afcChannelAllowance.availableAfcFrequencyInfos
1996                                                     .add(frequencyInfo);
1997                                 }
1998 
1999                                 break;
2000                             }
2001                             case "-c": {
2002                                 frequencyOrChannelArgumentIncluded = true;
2003                                 String channelsInput = getNextArgRequired();
2004 
2005                                 if (channelsInput.equals("none")) {
2006                                     break;
2007                                 }
2008 
2009                                 // parse channel list, and add it to the AfcChannelAllowance
2010                                 String[] unparsedChannels = channelsInput.split(":");
2011                                 afcChannelAllowance.availableAfcChannelInfos = new ArrayList<>();
2012 
2013                                 for (int i = 0; i < unparsedChannels.length; ++i) {
2014                                     String[] channelPieces = unparsedChannels[i].split(",");
2015 
2016                                     if (channelPieces.length != 3) {
2017                                         throw new IllegalArgumentException("Each channel in the "
2018                                                 + "available channel list should have 3 values, "
2019                                                 + "but found one with " + channelPieces.length);
2020                                     }
2021 
2022                                     WifiChip.AvailableAfcChannelInfo channelInfo = new
2023                                             WifiChip.AvailableAfcChannelInfo();
2024 
2025                                     channelInfo.globalOperatingClass =
2026                                             Integer.parseInt(channelPieces[0]);
2027                                     channelInfo.channelCfi = Integer.parseInt(channelPieces[1]);
2028                                     channelInfo.maxEirpDbm = Integer.parseInt(channelPieces[2]);
2029 
2030                                     afcChannelAllowance.availableAfcChannelInfos.add(channelInfo);
2031                                 }
2032 
2033                                 break;
2034                             }
2035                             default: {
2036                                 pw.println("Unrecognized command line argument.");
2037                                 return -1;
2038                             }
2039                         }
2040                     }
2041                     if (!expiryTimeArgumentIncluded) {
2042                         pw.println("Please include the -e flag to set the seconds until the "
2043                                 + "availability expires.");
2044                         return -1;
2045                     }
2046                     if (!frequencyOrChannelArgumentIncluded) {
2047                         pw.println("Please include at least one of the -f or -c flags to set the "
2048                                 + "frequency or channel availability.");
2049                         return -1;
2050                     }
2051 
2052                     ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
2053                     mWifiThreadRunner.post(() -> {
2054                         if (mWifiNative.setAfcChannelAllowance(afcChannelAllowance)) {
2055                             queue.offer("Successfully set the allowed AFC channels and "
2056                                     + "frequencies.");
2057                         } else {
2058                             queue.offer("Setting the allowed AFC channels and frequencies "
2059                                     + "failed.");
2060                         }
2061                     }, TAG + "#" + cmd);
2062 
2063                     // block until msg is received, or timed out
2064                     String msg = queue.poll(3000, TimeUnit.MILLISECONDS);
2065                     if (msg == null) {
2066                         pw.println("Setting the allowed AFC channels and frequencies timed out.");
2067                     } else {
2068                         pw.println(msg);
2069                     }
2070 
2071                     return 0;
2072                 }
2073                 case "get-cached-scan-data":
2074                     WifiScanner.ScanData scanData =
2075                             mWifiNative.getCachedScanResultsFromAllClientIfaces();
2076 
2077                     if (scanData.getResults().length > 0) {
2078                         pw.println("Successfully get cached scan data: ");
2079                         for (ScanResult scanResult : scanData.getResults()) {
2080                             pw.println(scanResult);
2081                         }
2082                     } else {
2083                         pw.println("Cached scan data is empty");
2084                     }
2085                     return 0;
2086                 case "configure-afc-server":
2087                     final String url = getNextArgRequired();
2088 
2089                     if (!url.startsWith("http")) {
2090                         pw.println("The required URL first argument is not a valid server URL for"
2091                                 + " a HTTP request.");
2092                         return -1;
2093                     }
2094 
2095                     String secondOption = getNextOption();
2096                     Map<String, String> requestProperties = new HashMap<>();
2097                     if (secondOption != null && secondOption.equals("-r")) {
2098 
2099                         String key = getNextArg();
2100                         while (key != null) {
2101 
2102                             String value = getNextArg();
2103                             // Check if there is a next value
2104                             if (value != null) {
2105                                 requestProperties.put(key, value);
2106                             } else {
2107                                 // Fail to proceed as there is no value given for the corresponding
2108                                 // key
2109                                 pw.println(
2110                                         "No value provided for the corresponding key " + key
2111                                                 + ". There must be an even number of request"
2112                                                 + " property arguments provided after the -r "
2113                                                 + "option.");
2114                                 return -1;
2115                             }
2116                             key = getNextArg();
2117                         }
2118 
2119                     } else {
2120                         pw.println("No -r option was provided as second argument so the HTTP "
2121                                 + "request will have no request properties.");
2122                     }
2123 
2124                     mWifiThreadRunner.post(() -> {
2125                         mAfcManager.setServerUrlAndRequestPropertyPairs(url, requestProperties);
2126                     }, TAG + "#" + cmd);
2127 
2128                     pw.println("The URL is set to " + url);
2129                     pw.println("The request properties are set to: ");
2130 
2131                     for (Map.Entry<String, String> requestProperty : requestProperties.entrySet()) {
2132                         pw.println("Key: " + requestProperty.getKey() + ", Value: "
2133                                 + requestProperty.getValue());
2134                     }
2135                     return 0;
2136                 case "set-mock-wifimodem-service":
2137                     String opt = null;
2138                     String serviceName = null;
2139                     while ((opt = getNextOption()) != null) {
2140                         switch (opt) {
2141                             case "-s": {
2142                                 serviceName = getNextArgRequired();
2143                                 break;
2144                             }
2145                             default:
2146                                 pw.println("set-mock-wifimodem-service requires '-s' option");
2147                                 return -1;
2148                         }
2149                     }
2150                     mWifiService.setMockWifiService(serviceName);
2151                     // The result will be checked, must print result "true"
2152                     pw.print("true");
2153                     return 0;
2154                 case "get-mock-wifimodem-service":
2155                     pw.print(mWifiNative.getMockWifiServiceName());
2156                     return 0;
2157                 case "set-mock-wifimodem-methods":
2158                     String methods = getNextArgRequired();
2159                     if (mWifiService.setMockWifiMethods(methods)) {
2160                         pw.print("true");
2161                     } else {
2162                         pw.print("fail to set mock method: " + methods);
2163                         return -1;
2164                     }
2165                     return 0;
2166                 case "force-overlay-config-value":
2167                     int uid = Binder.getCallingUid();
2168                     if (!mWifiInjector.getWifiPermissionsUtil()
2169                             .checkNetworkSettingsPermission(Binder.getCallingUid())) {
2170                         pw.println("current shell caller Uid " + uid
2171                                 + " Missing NETWORK_SETTINGS permission");
2172                         return -1;
2173                     }
2174                     WifiResourceCache resourceCache = mContext.getResourceCache();
2175                     String type = getNextArgRequired();
2176                     String overlayName = getNextArgRequired();
2177                     boolean isEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
2178                     switch (type) {
2179                         case "bool" -> {
2180                             boolean value = false;
2181                             if (isEnabled) {
2182                                 value = getNextArgRequiredTrueOrFalse("true", "false");
2183                                 resourceCache.overrideBooleanValue(overlayName, value);
2184                             } else {
2185                                 resourceCache.restoreBooleanValue(overlayName);
2186                             }
2187                         }
2188                         case "integer" -> {
2189                             int value = 0;
2190                             if (isEnabled) {
2191                                 value = Integer.parseInt(getNextArgRequired());
2192                                 resourceCache.overrideIntegerValue(overlayName, value);
2193                             } else {
2194                                 resourceCache.restoreIntegerValue(overlayName);
2195                             }
2196                         }
2197                         default -> {
2198                             pw.print("require a valid type of the overlay");
2199                             return -1;
2200                         }
2201                     }
2202                     pw.println("true");
2203                     return 0;
2204                 case "get-overlay-config-values":
2205                     mContext.getResourceCache().dump(pw);
2206                     return 0;
2207                 default:
2208                     return handleDefaultCommands(cmd);
2209             }
2210         } catch (IllegalArgumentException e) {
2211             pw.println("Invalid args for " + cmd + ": " + e);
2212             return -1;
2213         } catch (Exception e) {
2214             pw.println("Exception while executing WifiShellCommand: ");
2215             e.printStackTrace(pw);
2216             return -1;
2217         }
2218     }
2219 
getNextArgRequiredTrueOrFalse(String trueString, String falseString)2220     private boolean getNextArgRequiredTrueOrFalse(String trueString, String falseString)
2221             throws IllegalArgumentException {
2222         String nextArg = getNextArgRequired();
2223         if (trueString.equals(nextArg)) {
2224             return true;
2225         } else if (falseString.equals(nextArg)) {
2226             return false;
2227         } else {
2228             throw new IllegalArgumentException("Expected '" + trueString + "' or '" + falseString
2229                     + "' as next arg but got '" + nextArg + "'");
2230         }
2231     }
2232 
buildWifiConfiguration(PrintWriter pw)2233     private WifiConfiguration buildWifiConfiguration(PrintWriter pw) {
2234         String ssid = getNextArgRequired();
2235         String type = getNextArgRequired();
2236         WifiConfiguration configuration = new WifiConfiguration();
2237         // Wrap the SSID in double quotes for UTF-8. The quotes may be removed if the SSID is in
2238         // hexadecimal digits, specified by the [-x] option below.
2239         configuration.SSID = "\"" + ssid + "\"";
2240         if (TextUtils.equals(type, "wpa3")) {
2241             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
2242             configuration.preSharedKey = "\"" + getNextArgRequired() + "\"";
2243         } else if (TextUtils.equals(type, "wpa2")) {
2244             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
2245             configuration.preSharedKey = "\"" + getNextArgRequired() + "\"";
2246         } else if (TextUtils.equals(type, "owe")) {
2247             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
2248         } else if (TextUtils.equals(type, "open")) {
2249             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
2250         } else if (TextUtils.equals(type, "dpp")) {
2251             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_DPP);
2252         } else if (TextUtils.equals(type, "wep")) {
2253             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
2254             String password = getNextArgRequired();
2255             // WEP-40, WEP-104, and WEP-256
2256             if ((password.length() == 10 || password.length() == 26 || password.length() == 58)
2257                     && password.matches("[0-9A-Fa-f]*")) {
2258                 configuration.wepKeys[0] = password;
2259             } else {
2260                 configuration.wepKeys[0] = '"' + password + '"';
2261             }
2262         } else {
2263             throw new IllegalArgumentException("Unknown network type " + type);
2264         }
2265         String option = getNextOption();
2266         while (option != null) {
2267             if (option.equals("-x")) {
2268                 configuration.SSID = ssid;
2269             } else if (option.equals("-m")) {
2270                 configuration.meteredOverride = METERED_OVERRIDE_METERED;
2271             } else if (option.equals("-d")) {
2272                 configuration.allowAutojoin = false;
2273             } else if (option.equals("-b")) {
2274                 configuration.BSSID = getNextArgRequired();
2275             } else if (option.equals("-r")) {
2276                 String macRandomizationScheme = getNextArgRequired();
2277                 if (macRandomizationScheme.equals("auto")) {
2278                     configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO;
2279                 } else if (macRandomizationScheme.equals("none")) {
2280                     configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
2281                 } else if (macRandomizationScheme.equals("persistent")) {
2282                     configuration.macRandomizationSetting =
2283                             WifiConfiguration.RANDOMIZATION_PERSISTENT;
2284                 } else if (macRandomizationScheme.equals("non_persistent")) {
2285                     if (SdkLevel.isAtLeastS()) {
2286                         configuration.macRandomizationSetting =
2287                                 WifiConfiguration.RANDOMIZATION_NON_PERSISTENT;
2288                     } else {
2289                         throw new IllegalArgumentException(
2290                                 "-r non_persistent MAC randomization not supported before S");
2291                     }
2292                 }
2293             } else if (option.equals("-h")) {
2294                 configuration.hiddenSSID = true;
2295             } else if (option.equals("-p")) {
2296                 configuration.shared = false;
2297             } else {
2298                 pw.println("Ignoring unknown option " + option);
2299             }
2300             option = getNextOption();
2301         }
2302         return configuration;
2303     }
2304 
buildSoftApConfiguration(PrintWriter pw)2305     private SoftApConfiguration buildSoftApConfiguration(PrintWriter pw) {
2306         String ssidStr = getNextArgRequired();
2307         String type = getNextArgRequired();
2308         SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
2309         configBuilder.setSsid(ssidStr);
2310         if (TextUtils.equals(type, "wpa2")) {
2311             configBuilder.setPassphrase(getNextArgRequired(),
2312                     SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
2313         } else if (TextUtils.equals(type, "wpa3")) {
2314             configBuilder.setPassphrase(getNextArgRequired(),
2315                     SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
2316         } else if (TextUtils.equals(type, "wpa3_transition")) {
2317             configBuilder.setPassphrase(getNextArgRequired(),
2318                     SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
2319         } else if (TextUtils.equals(type, "open")) {
2320             configBuilder.setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN);
2321         } else if (TextUtils.equals(type, "owe_transition")) {
2322             configBuilder.setPassphrase(null,
2323                     SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION);
2324         } else if (TextUtils.equals(type, "owe")) {
2325             configBuilder.setPassphrase(null,
2326                     SoftApConfiguration.SECURITY_TYPE_WPA3_OWE);
2327         } else {
2328             throw new IllegalArgumentException("Unknown network type " + type);
2329         }
2330         String option = getNextOption();
2331         while (option != null) {
2332             if (option.equals("-b")) {
2333                 String preferredBand = getNextArgRequired();
2334                 if (preferredBand.equals("2")) {
2335                     configBuilder.setBand(SoftApConfiguration.BAND_2GHZ);
2336                 } else if (preferredBand.equals("5")) {
2337                     configBuilder.setBand(SoftApConfiguration.BAND_5GHZ);
2338                 } else if (preferredBand.equals("6")) {
2339                     configBuilder.setBand(SoftApConfiguration.BAND_6GHZ);
2340                 } else if (preferredBand.equals("any")) {
2341                     configBuilder.setBand(SoftApConfiguration.BAND_2GHZ
2342                             | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ);
2343                 } else if (preferredBand.startsWith("bridged")) {
2344                     if (!SdkLevel.isAtLeastS()) {
2345                         throw new IllegalArgumentException(
2346                                "-b bridged* option is not supported before S");
2347                     }
2348                     switch (preferredBand) {
2349                         case "bridged":
2350                             // fall through
2351                         case "bridged_2_5":
2352                             configBuilder.setBands(new int[] {
2353                                     SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ});
2354                             break;
2355                         case "bridged_2_6":
2356                             configBuilder.setBands(new int[] {
2357                                     SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_6GHZ});
2358                             break;
2359                         case "bridged_5_6":
2360                             configBuilder.setBands(new int[] {
2361                                     SoftApConfiguration.BAND_5GHZ, SoftApConfiguration.BAND_6GHZ});
2362                             break;
2363                         default:
2364                             throw new IllegalArgumentException("Invalid bridged band option "
2365                                     + preferredBand);
2366                     }
2367                 } else {
2368                     throw new IllegalArgumentException("Invalid band option " + preferredBand);
2369                 }
2370             } else if (SdkLevel.isAtLeastT() && option.equals("-x")) {
2371                 configBuilder.setWifiSsid(WifiSsid.fromString(ssidStr));
2372             } else if (option.equals("-f")) {
2373                 SparseIntArray channels = new SparseIntArray();
2374                 while (getRemainingArgsCount() > 0) {
2375                     int apChannelMHz;
2376                     try {
2377                         apChannelMHz = Integer.parseInt(getNextArgRequired());
2378                     } catch (NumberFormatException e) {
2379                         throw new IllegalArgumentException(
2380                                 "-f option requires a valid channel frequency");
2381                     }
2382                     channels.put(ApConfigUtil.convertFrequencyToBand(apChannelMHz),
2383                             ScanResult.convertFrequencyMhzToChannelIfSupported(apChannelMHz));
2384                 }
2385                 if (channels.size() == 0) {
2386                     throw new IllegalArgumentException(
2387                             "-f option requires a valid channel frequency atleast");
2388                 }
2389                 if (SdkLevel.isAtLeastS()) {
2390                     configBuilder.setChannels(channels);
2391                 } else {
2392                     if (channels.size() > 1) {
2393                         throw new IllegalArgumentException(
2394                                 "dual channels are not supported before S");
2395                     }
2396                     configBuilder.setChannel(channels.valueAt(0), channels.keyAt(0));
2397                 }
2398             } else if (option.equals("-w")) {
2399                 String bandwidth = getNextArgRequired();
2400                 if (bandwidth.equals("20")) {
2401                     configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ);
2402                 } else if (bandwidth.equals("40")) {
2403                     configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_40MHZ);
2404                 } else if (bandwidth.equals("80")) {
2405                     configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_80MHZ);
2406                 } else if (bandwidth.equals("160")) {
2407                     configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_160MHZ);
2408                 } else if (bandwidth.equals("320")) {
2409                     configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_320MHZ);
2410                 } else {
2411                     throw new IllegalArgumentException("Invalid bandwidth option " + bandwidth);
2412                 }
2413             } else {
2414                 pw.println("Ignoring unknown option " + option);
2415             }
2416             option = getNextOption();
2417         }
2418         return configBuilder.build();
2419     }
2420 
buildSuggestion(PrintWriter pw)2421     private WifiNetworkSuggestion buildSuggestion(PrintWriter pw) {
2422         String ssid = getNextArgRequired();
2423         String type = getNextArgRequired();
2424         WifiNetworkSuggestion.Builder suggestionBuilder =
2425                 new WifiNetworkSuggestion.Builder();
2426         suggestionBuilder.setSsid(ssid);
2427         if (TextUtils.equals(type, "wpa3")) {
2428             suggestionBuilder.setWpa3Passphrase(getNextArgRequired());
2429         } else if (TextUtils.equals(type, "wpa2")) {
2430             suggestionBuilder.setWpa2Passphrase(getNextArgRequired());
2431         } else if (TextUtils.equals(type, "owe")) {
2432             suggestionBuilder.setIsEnhancedOpen(true);
2433         } else if (TextUtils.equals(type, "open")) {
2434             // nothing to do.
2435         } else {
2436             throw new IllegalArgumentException("Unknown network type " + type);
2437         }
2438         boolean isCarrierMerged = false;
2439         String option = getNextOption();
2440         while (option != null) {
2441             if (option.equals("-u")) {
2442                 suggestionBuilder.setUntrusted(true);
2443             } else if (option.equals("-o")) {
2444                 if (SdkLevel.isAtLeastS()) {
2445                     suggestionBuilder.setOemPaid(true);
2446                 } else {
2447                     throw new IllegalArgumentException(
2448                             "-o OEM paid suggestions not supported before S");
2449                 }
2450             } else if (option.equals("-p")) {
2451                 if (SdkLevel.isAtLeastS()) {
2452                     suggestionBuilder.setOemPrivate(true);
2453                 } else {
2454                     throw new IllegalArgumentException(
2455                             "-p OEM private suggestions not supported before S");
2456                 }
2457             } else if (option.equals("-m")) {
2458                 suggestionBuilder.setIsMetered(true);
2459             } else if (option.equals("-s")) {
2460                 suggestionBuilder.setCredentialSharedWithUser(true);
2461             } else if (option.equals("-d")) {
2462                 suggestionBuilder.setIsInitialAutojoinEnabled(false);
2463             } else if (option.equals("-b")) {
2464                 suggestionBuilder.setBssid(MacAddress.fromString(getNextArgRequired()));
2465             } else if (option.equals("-r")) {
2466                 if (SdkLevel.isAtLeastS()) {
2467                     suggestionBuilder.setMacRandomizationSetting(
2468                             WifiNetworkSuggestion.RANDOMIZATION_NON_PERSISTENT);
2469                 } else {
2470                     throw new IllegalArgumentException(
2471                             "-r non_persistent MAC randomization not supported before S");
2472                 }
2473             } else if (option.equals("-a")) {
2474                 if (SdkLevel.isAtLeastS()) {
2475                     isCarrierMerged = true;
2476                 } else {
2477                     throw new IllegalArgumentException("-a option is not supported before S");
2478                 }
2479             } else if (option.equals("-i")) {
2480                 if (SdkLevel.isAtLeastS()) {
2481                     int subId = Integer.parseInt(getNextArgRequired());
2482                     suggestionBuilder.setSubscriptionId(subId);
2483                 } else {
2484                     throw new IllegalArgumentException(
2485                             "-i subscription ID option is not supported before S");
2486                 }
2487             } else if (option.equals("-c")) {
2488                 int carrierId = Integer.parseInt(getNextArgRequired());
2489                 suggestionBuilder.setCarrierId(carrierId);
2490             } else if (option.equals("-h")) {
2491                 suggestionBuilder.setIsHiddenSsid(true);
2492             } else {
2493                 pw.println("Ignoring unknown option " + option);
2494             }
2495             option = getNextOption();
2496         }
2497         WifiNetworkSuggestion suggestion = suggestionBuilder.build();
2498         if (isCarrierMerged) {
2499             if (suggestion.wifiConfiguration.subscriptionId
2500                     == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
2501                 pw.println("Carrier merged network must have valid subscription Id");
2502                 return null;
2503             }
2504             suggestion.wifiConfiguration.carrierMerged = true;
2505         }
2506         return suggestion;
2507     }
2508 
buildNetworkRequest(PrintWriter pw)2509     private Pair<String, NetworkRequest> buildNetworkRequest(PrintWriter pw) {
2510         String firstOpt = getNextOption();
2511         boolean isGlob = "-g".equals(firstOpt);
2512         boolean noSsid = "-s".equals(firstOpt);
2513         String ssid = noSsid ? "NoSsid" : getNextArgRequired();
2514         String type = noSsid ? null : getNextArgRequired();
2515         WifiNetworkSpecifier.Builder specifierBuilder =
2516                 new WifiNetworkSpecifier.Builder();
2517         if (isGlob) {
2518             specifierBuilder.setSsidPattern(
2519                     new PatternMatcher(ssid, PatternMatcher.PATTERN_ADVANCED_GLOB));
2520         } else {
2521             if (ssid != null && !noSsid) specifierBuilder.setSsid(ssid);
2522         }
2523         if (type != null) {
2524             if (TextUtils.equals(type, "wpa3")) {
2525                 specifierBuilder.setWpa3Passphrase(getNextArgRequired());
2526             } else if (TextUtils.equals(type, "wpa3_transition")) {
2527                 specifierBuilder.setWpa3Passphrase(getNextArgRequired());
2528             } else if (TextUtils.equals(type, "wpa2")) {
2529                 specifierBuilder.setWpa2Passphrase(getNextArgRequired());
2530             } else if (TextUtils.equals(type, "owe")) {
2531                 specifierBuilder.setIsEnhancedOpen(true);
2532             } else if (TextUtils.equals(type, "open")) {
2533                 // nothing to do.
2534             } else {
2535                 throw new IllegalArgumentException("Unknown network type " + type);
2536             }
2537         }
2538         String bssid = null;
2539         String option = getNextOption();
2540         String ssidKey = ssid;
2541         boolean nullBssid = false;
2542         boolean hasInternet = false;
2543         while (option != null) {
2544             if (option.equals("-b")) {
2545                 bssid = getNextArgRequired();
2546             } else if (option.equals("-n")) {
2547                 nullBssid = true;
2548             } else if (option.equals("-d")) {
2549                 String band = getNextArgRequired();
2550                 ssidKey = ssidKey + "_" + band + "g";
2551                 if (band.equals("2")) {
2552                     specifierBuilder.setBand(ScanResult.WIFI_BAND_24_GHZ);
2553                 } else if (band.equals("5")) {
2554                     specifierBuilder.setBand(ScanResult.WIFI_BAND_5_GHZ);
2555                 } else if (band.equals("6")) {
2556                     specifierBuilder.setBand(ScanResult.WIFI_BAND_6_GHZ);
2557                 } else if (band.equals("60")) {
2558                     specifierBuilder.setBand(ScanResult.WIFI_BAND_60_GHZ);
2559                 } else {
2560                     throw new IllegalArgumentException("Unknown band " + band);
2561                 }
2562             } else if (option.equals("-i")) {
2563                 ssidKey = ssidKey + "_internet";
2564                 hasInternet = true;
2565             } else {
2566                 pw.println("Ignoring unknown option " + option);
2567             }
2568             option = getNextOption();
2569         }
2570         if (bssid != null && nullBssid) {
2571             throw new IllegalArgumentException("Invalid option combination: "
2572                     + "Should not use both -b and -n at the same time.");
2573         }
2574 
2575         // Permission approval bypass is only available to requests with both ssid & bssid set.
2576         // So, find scan result with the best rssi level to set in the request.
2577         if (bssid == null && !nullBssid && !noSsid) {
2578             ScanResult matchingScanResult =
2579                     mScanRequestProxy.getScanResults()
2580                             .stream()
2581                             .filter(s -> s.SSID.equals(ssid))
2582                             .max(Comparator.comparingInt(s -> s.level))
2583                             .orElse(null);
2584             if (matchingScanResult != null) {
2585                 bssid = matchingScanResult.BSSID;
2586             } else {
2587                 pw.println("No matching bssid found, request will need UI approval");
2588             }
2589         }
2590         if (bssid != null && !nullBssid) specifierBuilder.setBssid(MacAddress.fromString(bssid));
2591         NetworkRequest.Builder builder = new NetworkRequest.Builder()
2592                 .addTransportType(TRANSPORT_WIFI);
2593         if (hasInternet) {
2594             builder.addCapability(NET_CAPABILITY_INTERNET);
2595         } else {
2596             builder.removeCapability(NET_CAPABILITY_INTERNET);
2597         }
2598         return new Pair<String, NetworkRequest>(ssidKey,
2599                 builder.setNetworkSpecifier(specifierBuilder.build()).build());
2600     }
2601 
2602     @RequiresApi(Build.VERSION_CODES.S)
2603     @NonNull
buildCoexCellChannels()2604     private List<CoexUtils.CoexCellChannel> buildCoexCellChannels() {
2605         List<CoexUtils.CoexCellChannel> cellChannels = new ArrayList<>();
2606         while (getRemainingArgsCount() > 0) {
2607             final @Annotation.NetworkType int rat;
2608             final String ratArg = getNextArgRequired();
2609             if (TextUtils.equals(ratArg, "lte")) {
2610                 rat = TelephonyManager.NETWORK_TYPE_LTE;
2611             } else if (TextUtils.equals(ratArg, "nr")) {
2612                 rat = TelephonyManager.NETWORK_TYPE_NR;
2613             } else {
2614                 throw new IllegalArgumentException("Unknown rat type " + ratArg);
2615             }
2616             final int band = Integer.parseInt(getNextArgRequired());
2617             if (band < 1 || band > 261) {
2618                 throw new IllegalArgumentException("Band is " + band
2619                         + " but should be a value from 1 to 261");
2620             }
2621             final int downlinkFreqKhz = Integer.parseInt(getNextArgRequired());
2622             if (downlinkFreqKhz < 0 && downlinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) {
2623                 throw new IllegalArgumentException("Downlink frequency is " + downlinkFreqKhz
2624                         + " but should be >= 0 or UNKNOWN: "
2625                         + PhysicalChannelConfig.FREQUENCY_UNKNOWN);
2626             }
2627             final int downlinkBandwidthKhz = Integer.parseInt(getNextArgRequired());
2628             if (downlinkBandwidthKhz <= 0
2629                     && downlinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
2630                 throw new IllegalArgumentException("Downlink bandwidth is " + downlinkBandwidthKhz
2631                         + " but should be > 0 or UNKNOWN: "
2632                         + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN);
2633             }
2634             final int uplinkFreqKhz = Integer.parseInt(getNextArgRequired());
2635             if (uplinkFreqKhz < 0 && uplinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) {
2636                 throw new IllegalArgumentException("Uplink frequency is " + uplinkFreqKhz
2637                         + " but should be >= 0 or UNKNOWN: "
2638                         + PhysicalChannelConfig.FREQUENCY_UNKNOWN);
2639             }
2640             final int uplinkBandwidthKhz = Integer.parseInt(getNextArgRequired());
2641             if (uplinkBandwidthKhz <= 0
2642                     && uplinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
2643                 throw new IllegalArgumentException("Uplink bandwidth is " + uplinkBandwidthKhz
2644                         + " but should be > 0 or UNKNOWN: "
2645                         + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN);
2646             }
2647             cellChannels.add(new CoexUtils.CoexCellChannel(rat, band,
2648                     downlinkFreqKhz, downlinkBandwidthKhz, uplinkFreqKhz, uplinkBandwidthKhz,
2649                     SubscriptionManager.INVALID_SUBSCRIPTION_ID));
2650         }
2651         return cellChannels;
2652     }
2653 
sendLinkProbe(PrintWriter pw)2654     private int sendLinkProbe(PrintWriter pw) throws InterruptedException {
2655         // Note: should match WifiNl80211Manager#SEND_MGMT_FRAME_TIMEOUT_MS
2656         final int sendMgmtFrameTimeoutMs = 1000;
2657 
2658         ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
2659         mWifiThreadRunner.post(() ->
2660                 mActiveModeWarden.getPrimaryClientModeManager().probeLink(new LinkProbeCallback() {
2661                     @Override
2662                     public void onAck(int elapsedTimeMs) {
2663                         queue.offer("Link probe succeeded after " + elapsedTimeMs + " ms");
2664                     }
2665 
2666                     @Override
2667                     public void onFailure(int reason) {
2668                         queue.offer("Link probe failed with reason "
2669                                 + LinkProbeCallback.failureReasonToString(reason));
2670                     }
2671                 }, -1), TAG + "#sendLinkProbe");
2672 
2673         // block until msg is received, or timed out
2674         String msg = queue.poll(sendMgmtFrameTimeoutMs + 1000, TimeUnit.MILLISECONDS);
2675         if (msg == null) {
2676             pw.println("Link probe timed out");
2677         } else {
2678             pw.println(msg);
2679         }
2680         return 0;
2681     }
2682 
isApChannelMHzValid(PrintWriter pw, int apChannelMHz)2683     private boolean isApChannelMHzValid(PrintWriter pw, int apChannelMHz) {
2684         int[] allowed2gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
2685         int[] allowed5gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
2686         int[] allowed5gDfsFreq =
2687             mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
2688         int[] allowed6gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ);
2689         int[] allowed60gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_60_GHZ);
2690         if (allowed2gFreq == null) {
2691             allowed2gFreq = new int[0];
2692         }
2693         if (allowed5gFreq == null) {
2694             allowed5gFreq = new int[0];
2695         }
2696         if (allowed5gDfsFreq == null) {
2697             allowed5gDfsFreq = new int[0];
2698         }
2699         if (allowed6gFreq == null) {
2700             allowed6gFreq = new int[0];
2701         }
2702         if (allowed60gFreq == null) {
2703             allowed60gFreq = new int[0];
2704         }
2705         pw.println("2G freq: " + Arrays.toString(allowed2gFreq));
2706         pw.println("5G freq: " + Arrays.toString(allowed5gFreq));
2707         pw.println("5G DFS: " + Arrays.toString(allowed5gDfsFreq));
2708         pw.println("6G freq: " + Arrays.toString(allowed6gFreq));
2709         pw.println("60G freq: " + Arrays.toString(allowed60gFreq));
2710         return (Arrays.binarySearch(allowed2gFreq, apChannelMHz) >= 0
2711                 || Arrays.binarySearch(allowed5gFreq, apChannelMHz) >= 0
2712                 || Arrays.binarySearch(allowed5gDfsFreq, apChannelMHz) >= 0)
2713                 || Arrays.binarySearch(allowed6gFreq, apChannelMHz) >= 0
2714                 || Arrays.binarySearch(allowed60gFreq, apChannelMHz) >= 0;
2715     }
2716 
waitForWifiEnabled(boolean enabled)2717     private void waitForWifiEnabled(boolean enabled) throws InterruptedException {
2718         CountDownLatch countDownLatch = new CountDownLatch(1);
2719         BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
2720             @Override
2721             public void onReceive(Context context, Intent intent) {
2722                 String action = intent.getAction();
2723                 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
2724                     int state = mWifiService.getWifiEnabledState();
2725                     if ((enabled && state == WIFI_STATE_ENABLED)
2726                             || (!enabled && state == WIFI_STATE_DISABLED)) {
2727                         countDownLatch.countDown();
2728                     }
2729                 }
2730             }
2731         };
2732         IntentFilter filter = new IntentFilter();
2733         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
2734         mContext.registerReceiver(broadcastReceiver, filter);
2735         mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled);
2736         countDownLatch.await(5000, TimeUnit.MILLISECONDS);
2737         mContext.unregisterReceiver(broadcastReceiver);
2738     }
2739 
printWifiInfo(PrintWriter pw, WifiInfo info)2740     private void printWifiInfo(PrintWriter pw, WifiInfo info) {
2741         if (info.getSupplicantState() != SupplicantState.COMPLETED) {
2742             pw.println("Wifi is not connected");
2743             return;
2744         }
2745         pw.println("Wifi is connected to " + info.getSSID());
2746         pw.println("WifiInfo: " + info);
2747         // additional diagnostics not printed by WifiInfo.toString()
2748         pw.println("successfulTxPackets: " + info.txSuccess);
2749         pw.println("successfulTxPacketsPerSecond: " + info.getSuccessfulTxPacketsPerSecond());
2750         pw.println("retriedTxPackets: " + info.txRetries);
2751         pw.println("retriedTxPacketsPerSecond: " + info.getRetriedTxPacketsPerSecond());
2752         pw.println("lostTxPackets: " + info.txBad);
2753         pw.println("lostTxPacketsPerSecond: " + info.getLostTxPacketsPerSecond());
2754         pw.println("successfulRxPackets: " + info.rxSuccess);
2755         pw.println("successfulRxPacketsPerSecond: " + info.getSuccessfulRxPacketsPerSecond());
2756     }
2757 
printStatus(PrintWriter pw)2758     private void printStatus(PrintWriter pw) {
2759         boolean wifiEnabled = mWifiService.getWifiEnabledState() == WIFI_STATE_ENABLED;
2760         pw.println("Wifi is " + (wifiEnabled ? "enabled" : "disabled"));
2761         pw.println("Wifi scanning is "
2762                 + (mWifiService.isScanAlwaysAvailable()
2763                 ? "always available" : "only available when wifi is enabled"));
2764         if (!wifiEnabled) {
2765             return;
2766         }
2767         if (Binder.getCallingUid() != Process.ROOT_UID) {
2768             // not privileged, just dump the primary client mode manager manager status
2769             // (public API contents).
2770             pw.println("==== Primary ClientModeManager instance ====");
2771             printWifiInfo(pw, mWifiService.getConnectionInfo(SHELL_PACKAGE_NAME, null));
2772         } else {
2773             // privileged, dump out all the client mode manager manager statuses
2774             for (ClientModeManager cm : mActiveModeWarden.getClientModeManagers()) {
2775                 pw.println("==== ClientModeManager instance: " + cm + " ====");
2776                 WifiInfo info = cm.getConnectionInfo();
2777                 printWifiInfo(pw, info);
2778                 if (info.getSupplicantState() != SupplicantState.COMPLETED) {
2779                     continue;
2780                 }
2781                 Network network = cm.getCurrentNetwork();
2782                 NetworkCapabilities capabilities =
2783                         mConnectivityManager.getNetworkCapabilities(network);
2784                 pw.println("NetworkCapabilities: " + capabilities);
2785             }
2786         }
2787     }
2788 
onHelpNonPrivileged(PrintWriter pw)2789     private void onHelpNonPrivileged(PrintWriter pw) {
2790         pw.println("  get-country-code");
2791         pw.println("    Gets country code as a two-letter string");
2792         pw.println("  set-wifi-enabled enabled|disabled");
2793         pw.println("    Enables/disables Wifi on this device.");
2794         pw.println("  set-scan-always-available enabled|disabled");
2795         pw.println("    Sets whether scanning should be available even when wifi is off.");
2796         pw.println("  connect-network <ssid> open|owe|wpa2|wpa3|wep [<passphrase>] [-x] [-m] "
2797                 + "[-d] [-b <bssid>] [-r auto|none|persistent|non_persistent]");
2798         pw.println("    Connect to a network with provided params and add to saved networks list");
2799         pw.println("    <ssid> - SSID of the network");
2800         pw.println("    open|owe|wpa2|wpa3|wep - Security type of the network.");
2801         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
2802         pw.println("           - 'open' - Open networks (Most prevalent)");
2803         pw.println("           - 'owe' - Enhanced open networks");
2804         pw.println("        - Use 'wpa2' or 'wpa3' or 'wep' for networks with passphrase");
2805         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
2806         pw.println("           - 'wpa3' - WPA-3 PSK networks");
2807         pw.println("           - 'wep'  - WEP network. Passphrase should be bytes in hex or encoded"
2808                 + " into String using UTF-8");
2809         pw.println("    -x - Specifies the SSID as hex digits instead of plain text");
2810         pw.println("    -m - Mark the network metered.");
2811         pw.println("    -d - Mark the network autojoin disabled.");
2812         pw.println("    -h - Mark the network hidden.");
2813         pw.println("    -p - Mark the network private (not shared).");
2814         pw.println("    -b <bssid> - Set specific BSSID.");
2815         pw.println("    -r auto|none|persistent|non_persistent - MAC randomization scheme for the"
2816                 + " network");
2817         pw.println("  add-network <ssid> open|owe|wpa2|wpa3|wep [<passphrase>] [-x] [-m] [-d] "
2818                 + "[-b <bssid>] [-r auto|none|persistent|non_persistent]");
2819         pw.println("    Add/update saved network with provided params");
2820         pw.println("    <ssid> - SSID of the network");
2821         pw.println("    open|owe|wpa2|wpa3|wep - Security type of the network.");
2822         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
2823         pw.println("           - 'open' - Open networks (Most prevalent)");
2824         pw.println("           - 'owe' - Enhanced open networks");
2825         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
2826         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
2827         pw.println("           - 'wpa3' - WPA-3 PSK networks");
2828         pw.println("           - 'wep'  - WEP network. Passphrase should be bytes in hex or encoded"
2829                 + " into String using UTF-8");
2830         pw.println("    -x - Specifies the SSID as hex digits instead of plain text");
2831         pw.println("    -m - Mark the network metered.");
2832         pw.println("    -d - Mark the network autojoin disabled.");
2833         pw.println("    -h - Mark the network hidden.");
2834         pw.println("    -p - Mark the network private (not shared).");
2835         pw.println("    -b <bssid> - Set specific BSSID.");
2836         pw.println("    -r auto|none|persistent|non_persistent - MAC randomization scheme for the"
2837                 + " network");
2838         pw.println("  list-scan-results");
2839         pw.println("    Lists the latest scan results");
2840         pw.println("  start-scan");
2841         pw.println("    Start a new scan");
2842         pw.println("  list-networks");
2843         pw.println("    Lists the saved networks");
2844         pw.println("  forget-network <networkId>");
2845         pw.println("    Remove the network mentioned by <networkId>");
2846         pw.println("        - Use list-networks to retrieve <networkId> for the network");
2847         pw.println("  status");
2848         pw.println("    Current wifi status");
2849         pw.println("  set-verbose-logging enabled|disabled [-l <verbose log level>]");
2850         pw.println("    Set the verbose logging enabled or disabled with log level");
2851         pw.println("      -l - verbose logging level");
2852         pw.println("           0 - verbose logging disabled");
2853         pw.println("           1 - verbose logging enabled");
2854         pw.println("           2 - verbose logging Show key mode");
2855         pw.println("           3 - verbose logging only for Wi-Fi Aware feature");
2856         pw.println("  is-verbose-logging");
2857         pw.println("    Check whether verbose logging enabled or disabled");
2858         pw.println("  start-restricting-auto-join-to-subscription-id subId");
2859         pw.println("    temporarily disable all wifi networks except merged carrier networks with"
2860                 + " the given subId");
2861         pw.println("  stop-restricting-auto-join-to-subscription-id");
2862         pw.println("    Undo the effects of "
2863                 + "start-restricting-auto-join-to-subscription-id");
2864         pw.println("  add-suggestion <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-u] [-o] [-p] [-m] "
2865                 + " [-s] [-d] [-b <bssid>] [-e] [-i] [-a <carrierId>] [-c <subscriptionId>]");
2866         pw.println("    Add a network suggestion with provided params");
2867         pw.println("    Use 'network-suggestions-set-user-approved " + SHELL_PACKAGE_NAME + " yes'"
2868                 +  " to approve suggestions added via shell (Needs root access)");
2869         pw.println("    <ssid> - SSID of the network");
2870         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
2871         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
2872         pw.println("           - 'open' - Open networks (Most prevalent)");
2873         pw.println("           - 'owe' - Enhanced open networks");
2874         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
2875         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
2876         pw.println("           - 'wpa3' - WPA-3 PSK networks");
2877         pw.println("    -u - Mark the suggestion untrusted.");
2878         pw.println("    -o - Mark the suggestion oem paid.");
2879         pw.println("    -p - Mark the suggestion oem private.");
2880         pw.println("    -m - Mark the suggestion metered.");
2881         pw.println("    -h - Mark the network hidden.");
2882         pw.println("    -s - Share the suggestion with user.");
2883         pw.println("    -d - Mark the suggestion autojoin disabled.");
2884         pw.println("    -b <bssid> - Set specific BSSID.");
2885         pw.println("    -r - Enable non_persistent randomization (disabled by default)");
2886         pw.println("    -a - Mark the suggestion carrier merged");
2887         pw.println("    -c <carrierId> - set carrier Id");
2888         pw.println("    -i <subscriptionId> - set subscription Id, if -a is used, "
2889                 + "this must be set");
2890         pw.println("  remove-suggestion <ssid> [-l]");
2891         pw.println("    Remove a network suggestion with provided SSID of the network");
2892         pw.println("    -l - Remove suggestion with lingering, if not set will disconnect "
2893                 + "immediately ");
2894         pw.println("  remove-all-suggestions");
2895         pw.println("    Removes all suggestions added via shell");
2896         pw.println("  list-suggestions");
2897         pw.println("    Lists the suggested networks added via shell");
2898         if (SdkLevel.isAtLeastS()) {
2899             pw.println("  set-coex-cell-channels [lte|nr <bandNumber 1-261> "
2900                     + "<downlinkFreqKhz or UNKNOWN: "
2901                     + PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> "
2902                     + "<downlinkBandwidthKhz or UNKNOWN: "
2903                     + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + "> "
2904                     + "<uplinkFreqKhz or UNKNOWN: "
2905                     + PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> "
2906                     + "<uplinkBandwidthKhz or UNKNOWN: "
2907                     + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + ">] ...");
2908             pw.println("    Sets a list of zero or more cell channels to use for coex calculations."
2909                     + " Actual device reported cell channels will be ignored until"
2910                     + " reset-coex-cell-channels is called.");
2911             pw.println("  reset-coex-cell-channels");
2912             pw.println("    Removes all cell channels set in set-coex-cell-channels and returns to "
2913                     + "listening on actual device reported cell channels");
2914             pw.println("  get-coex-cell-channels");
2915             pw.println("    Prints the cell channels being used for coex.");
2916         }
2917         pw.println("  set-connected-score <score>");
2918         pw.println("    Set connected wifi network score (to choose between LTE & Wifi for "
2919                 + "default route).");
2920         pw.println("    This turns off the active connected scorer (default or external).");
2921         pw.println("    Only works while connected to a wifi network. This score will stay in "
2922                 + "effect until you call reset-connected-score or the device disconnects from the "
2923                 + "current network.");
2924         pw.println("    <score> - Integer score should be in the range of 0 - 60");
2925         pw.println("  reset-connected-score");
2926         pw.println("    Turns on the default connected scorer.");
2927         pw.println("    Note: Will clear any external scorer set.");
2928         pw.println("  pmksa-flush <networkId>");
2929         pw.println("        - Flush the local PMKSA cache associated with the network id."
2930                 + " Use list-networks to retrieve <networkId> for the network");
2931         pw.println("  reload-resources");
2932         pw.println(
2933                 "    Reset the WiFi resources cache which will cause them to be reloaded next "
2934                         + "time they are accessed. Necessary if overlays are manually modified.");
2935         pw.println("  launch-dialog-simple [-t <title>] [-m <message>]"
2936                 + " [-l <url> <url_start> <url_end>] [-y <positive_button_text>]"
2937                 + " [-n <negative_button_text>] [-x <neutral_button_text>] [-c <timeout_millis>]");
2938         pw.println("    Launches a simple dialog and waits up to 15 seconds to"
2939                 + " print the response.");
2940         pw.println("    -t - Title");
2941         pw.println("    -m - Message");
2942         pw.println("    -l - URL of the message, with the start and end index inside the message");
2943         pw.println("    -y - Positive Button Text");
2944         pw.println("    -n - Negative Button Text");
2945         pw.println("    -x - Neutral Button Text");
2946         pw.println("    -c - Optional timeout in milliseconds");
2947         pw.println("    -s - Use the legacy dialog implementation on the system process");
2948         pw.println("  launch-dialog-p2p-invitation-sent <device_name> <pin> [-i <display_id>]");
2949         pw.println("    Launches a P2P Invitation Sent dialog.");
2950         pw.println("    <device_name> - Name of the device the invitation was sent to");
2951         pw.println("    <pin> - PIN for the invited device to input");
2952         pw.println("  launch-dialog-p2p-invitation-received <device_name> [-p] [-d <pin>] "
2953                 + "[-i <display_id>] [-c <timeout_millis>]");
2954         pw.println("    Launches a P2P Invitation Received dialog and waits up to 15 seconds to"
2955                 + " print the response.");
2956         pw.println("    <device_name> - Name of the device sending the invitation");
2957         pw.println("    -p - Show PIN input");
2958         pw.println("    -d - Display PIN <pin>");
2959         pw.println("    -i - Display ID");
2960         pw.println("    -c - Optional timeout in milliseconds");
2961         pw.println("  query-interface <uid> <package_name> STA|AP|AWARE|DIRECT [-new]");
2962         pw.println(
2963                 "    Query whether the specified could be created for the specified UID and "
2964                         + "package name, and if so - what other interfaces would be destroyed");
2965         pw.println("    -new - query for a new interfaces (otherwise an existing interface is ok");
2966         pw.println("  interface-priority-interactive-mode enable|disable|default");
2967         pw.println("    Enable or disable asking the user when there's an interface priority "
2968                 + "conflict, |default| implies using the device default behavior.");
2969         pw.println("  set-one-shot-screen-on-delay-ms <delayMs>");
2970         pw.println("    set the delay for the next screen-on connectivity scan in milliseconds.");
2971         pw.println("  set-network-selection-config <enabled|disabled> <enabled|disabled> "
2972                 + "-a <associated_network_selection_override>");
2973         pw.println("    set whether sufficiency check is enabled for screen off case "
2974                 + "(first arg), and screen on case (second arg)");
2975         pw.println("    -a - set as one of the int "
2976                 + "WifiNetworkSelectionConfig.ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ values:");
2977         pw.println("      0 - no override");
2978         pw.println("      1 - override to enabled");
2979         pw.println("      2 - override to disabled");
2980         pw.println("  set-ipreach-disconnect enabled|disabled");
2981         pw.println("    Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.");
2982         pw.println("  get-ipreach-disconnect");
2983         pw.println("    Gets setting of CMD_IP_REACHABILITY_LOST events triggering disconnects.");
2984         pw.println("  take-bugreport");
2985         pw.println(
2986                 "    take bugreport through betterBug. "
2987                         + "If it failed, take bugreport through bugreport manager.");
2988         pw.println("  get-allowed-channel [-b 1|6|7|8|15|16|31]");
2989         pw.println(
2990                 "    get allowed channels in each operation mode from wifiManager if available. "
2991                         + "Otherwise, it returns from wifiScanner.");
2992         pw.println("    -b - set the band in which channels are allowed");
2993         pw.println("       '1'  - band 2.4 GHz");
2994         pw.println("       '6'  - band 5 GHz with DFS channels");
2995         pw.println("       '7'  - band 2.4 and 5 GHz with DFS channels");
2996         pw.println("       '8'  - band 6 GHz");
2997         pw.println("       '15' - band 2.4, 5, and 6 GHz with DFS channels");
2998         pw.println("       '16' - band 60 GHz");
2999         pw.println("       '31' - band 2.4, 5, 6 and 60 GHz with DFS channels");
3000         pw.println("  get-cached-scan-data");
3001         pw.println("    Gets scan data cached by the firmware");
3002         pw.println("  force-overlay-config-value bool|integer <overlayName> enabled|disabled"
3003                 + "<configValue>");
3004         pw.println("    Force overlay to a specified value.");
3005         pw.println("    bool|integer   - specified the type of the overlay");
3006         pw.println("    <overlayName> - name of the overlay whose value is overridden.");
3007         pw.println("    enabled|disabled: enable the override or disable it and revert to using "
3008                 + "the built-in value.");
3009         pw.println("    <configValue> - override value of the overlay."
3010                 + "Must match the overlay type");
3011         pw.println("  get-overlay-config-values");
3012         pw.println("    Get current overlay value in resource cache.");
3013         pw.println("  get-softap-supported-features");
3014         pw.println("    Gets softap supported features. Will print 'wifi_softap_acs_supported'");
3015         pw.println("    and/or 'wifi_softap_wpa3_sae_supported',");
3016         pw.println("    and/or 'wifi_softap_bridged_ap_supported',");
3017         pw.println("    and/or 'wifi_softap_bridged_ap_with_sta_supported',");
3018         pw.println("    each on a separate line.");
3019     }
3020 
onHelpPrivileged(PrintWriter pw)3021     private void onHelpPrivileged(PrintWriter pw) {
3022         pw.println("  set-poll-rssi-interval-msecs <int>");
3023         pw.println("    Sets the interval between RSSI polls to <int> milliseconds.");
3024         pw.println("  get-poll-rssi-interval-msecs");
3025         pw.println("    Gets current interval between RSSI polls, in milliseconds.");
3026         pw.println("  force-hi-perf-mode enabled|disabled");
3027         pw.println("    Sets whether hi-perf mode is forced or left for normal operation.");
3028         pw.println("  force-low-latency-mode enabled|disabled");
3029         pw.println("    Sets whether low latency mode is forced or left for normal operation.");
3030         pw.println("  network-suggestions-set-user-approved <package name> yes|no");
3031         pw.println("    Sets whether network suggestions from the app is approved or not.");
3032         pw.println("  network-suggestions-has-user-approved <package name>");
3033         pw.println("    Queries whether network suggestions from the app is approved or not.");
3034         pw.println("  imsi-protection-exemption-set-user-approved-for-carrier <carrier id> yes|no");
3035         pw.println("    Sets whether Imsi protection exemption for carrier is approved or not");
3036         pw.println("  imsi-protection-exemption-has-user-approved-for-carrier <carrier id>");
3037         pw.println("    Queries whether Imsi protection exemption for carrier is approved or not");
3038         pw.println("  imsi-protection-exemption-clear-user-approved-for-carrier <carrier id>");
3039         pw.println("    Clear the user choice on Imsi protection exemption for carrier");
3040         pw.println("  network-requests-remove-user-approved-access-points <package name>");
3041         pw.println("    Removes all user approved network requests for the app.");
3042         pw.println("  clear-user-disabled-networks");
3043         pw.println("    Clears the user disabled networks list.");
3044         pw.println("  send-link-probe");
3045         pw.println("    Manually triggers a link probe.");
3046         pw.println(
3047                 "  start-softap <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition)"
3048                         + " <passphrase> [-b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6]"
3049                         + " [-x] [-w 20|40|80|160|320] [-f <int> [<int>]]");
3050         pw.println("    Start softap with provided params");
3051         pw.println("    Note that the shell command doesn't activate internet tethering. In some "
3052                 + "devices, internet sharing is possible when Wi-Fi STA is also enabled and is"
3053                 + "associated to another AP with internet access.");
3054         pw.println("    <ssid> - SSID of the network");
3055         pw.println("    open|wpa2|wpa3|wpa3_transition|owe|owe_transition - Security type of the "
3056                 + "network.");
3057         pw.println("        - Use 'open', 'owe', 'owe_transition' for networks with no passphrase");
3058         pw.println("        - Use 'wpa2', 'wpa3', 'wpa3_transition' for networks with passphrase");
3059         pw.println("    -b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6 - select the"
3060                 + " preferred bands.");
3061         pw.println("        - Use '2' to select 2.4GHz band as the preferred band");
3062         pw.println("        - Use '5' to select 5GHz band as the preferred band");
3063         pw.println("        - Use '6' to select 6GHz band as the preferred band");
3064         pw.println("        - Use 'any' to indicate no band preference");
3065         pw.println("        - Use 'bridged' to indicate bridged AP which enables APs on both "
3066                 + "2.4G + 5G");
3067         pw.println("        - Use 'bridged_2_5' to indicate bridged AP which enables APs on both "
3068                 + "2.4G + 5G");
3069         pw.println("        - Use 'bridged_2_6' to indicate bridged AP which enables APs on both "
3070                 + "2.4G + 6G");
3071         pw.println("        - Use 'bridged_5_6' to indicate bridged AP which enables APs on both "
3072                 + "5G + 6G");
3073         pw.println("    Note: If the band option is not provided, 2.4GHz is the preferred band.");
3074         pw.println("          The exact channel is auto-selected by FW unless overridden by "
3075                 + "force-softap-channel command or '-f <int> <int>' option");
3076         pw.println("    -f <int> <int> - force exact channel frequency for operation channel");
3077         pw.println("    Note: -f <int> <int> - must be the last option");
3078         pw.println("          For example:");
3079         pw.println("          Use '-f 2412' to enable single Soft Ap on 2412");
3080         pw.println("          Use '-f 2412 5745' to enable bridged dual Soft Ap on 2412 and 5745");
3081         pw.println("    -x - Specifies the SSID as hex digits instead of plain text (T and above)");
3082         pw.println("    -w 20|40|80|160|320 - select the maximum channel bandwidth (MHz)");
3083         pw.println("  stop-softap");
3084         pw.println("    Stop softap (hotspot)");
3085         pw.println("  force-softap-band enabled <int> | disabled");
3086         pw.println("    Forces soft AP band to 2|5|6");
3087         pw.println("  force-softap-channel enabled <int> | disabled [-w <maxBandwidth>]");
3088         pw.println("    Sets whether soft AP channel is forced to <int> MHz [-w <maxBandwidth>]");
3089         pw.println("        -w 0|20|40|80|160|320 - select the maximum channel bandwidth (MHz)");
3090         pw.println("         Note: If the bandwidth option is not provided or set to 0, framework"
3091                 + " will set the maximum bandwidth to auto, allowing HAL to select the bandwidth");
3092         pw.println("    or left for normal   operation.");
3093         pw.println("  force-country-code enabled <two-letter code> | disabled ");
3094         pw.println("    Sets country code to <two-letter code> or left for normal value");
3095         pw.println("    or '00' for forcing to world mode country code");
3096         pw.println("  set-wifi-watchdog enabled|disabled");
3097         pw.println("    Sets whether wifi watchdog should trigger recovery");
3098         pw.println("  get-wifi-watchdog");
3099         pw.println("    Gets setting of wifi watchdog trigger recovery.");
3100         pw.println("  settings-reset");
3101         pw.println("    Initiates wifi settings reset");
3102         pw.println("  allow-root-to-get-local-only-cmm enabled|disabled");
3103         pw.println("    sets whether the shell running as root could use the local-only secondary "
3104                 + "STA");
3105         pw.println("  add-request [-g] [-i] [-n] [-s] <ssid> open|owe|wpa2|wpa3 [<passphrase>]"
3106                 + " [-b <bssid>] [-d <band=2|5|6|60>]");
3107         pw.println("    Add a network request with provided params");
3108         pw.println("    Use 'network-requests-set-user-approved android yes'"
3109                 +  " to pre-approve requests added via rooted shell (Not persisted)");
3110         pw.println("    -g - Marks the following SSID as a glob pattern");
3111         pw.println("    <ssid> - SSID of the network, or glob pattern if -g is present");
3112         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
3113         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
3114         pw.println("           - 'open' - Open networks (Most prevalent)");
3115         pw.println("           - 'owe' - Enhanced open networks");
3116         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
3117         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
3118         pw.println("           - 'wpa3' - WPA-3 PSK networks");
3119         pw.println("    -b <bssid> - Set specific BSSID.");
3120         pw.println("    -i Set internet capability.");
3121         pw.println("    -d Specify the band of access point: 2, 5, 6, or 60");
3122         pw.println("    -s No SSID provided, to be chosen by network selection.");
3123         pw.println("    -n - Prevent auto-selection of BSSID and force it to be null so that the "
3124                 + "request matches all BSSIDs.");
3125         pw.println("  remove-request <ssid>");
3126         pw.println("    Remove a network request with provided SSID of the network");
3127         pw.println("  remove-all-requests");
3128         pw.println("    Removes all active requests added via shell");
3129         pw.println("  list-requests");
3130         pw.println("    Lists the requested networks added via shell");
3131         pw.println("  network-requests-set-user-approved <package name> yes|no");
3132         pw.println("    Sets whether network requests from the app is approved or not.");
3133         pw.println("    Note: Only 1 such app can be approved from the shell at a time");
3134         pw.println("  network-requests-has-user-approved <package name>");
3135         pw.println("    Queries whether network requests from the app is approved or not.");
3136         pw.println("    Note: This only returns whether the app was set via the "
3137                 + "'network-requests-set-user-approved' shell command");
3138         pw.println("  list-all-suggestions");
3139         pw.println("    Lists all suggested networks on this device");
3140         pw.println("  list-suggestions-from-app <package name>");
3141         pw.println("    Lists the suggested networks from the app");
3142         pw.println("  set-emergency-callback-mode enabled|disabled");
3143         pw.println("    Sets whether Emergency Callback Mode (ECBM) is enabled.");
3144         pw.println("    Equivalent to receiving the "
3145                 + "TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED broadcast.");
3146         pw.println("  set-emergency-call-state enabled|disabled");
3147         pw.println("    Sets whether we are in the middle of an emergency call.");
3148         pw.println("Equivalent to receiving the "
3149                 + "TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED broadcast.");
3150         pw.println("  set-emergency-scan-request enabled|disabled");
3151         pw.println("    Sets whether there is a emergency scan request in progress.");
3152         pw.println("  network-suggestions-set-as-carrier-provider <packageName> yes|no");
3153         pw.println("    Set the <packageName> work as carrier provider or not.");
3154         pw.println("  is-network-suggestions-set-as-carrier-provider <packageName>");
3155         pw.println("    Queries whether the <packageName> is working as carrier provider or not.");
3156         pw.println("  remove-app-from-suggestion_database <packageName>");
3157         pw.println("    Remove <packageName> from the suggestion database, all suggestions and user"
3158                 + " approval will be deleted, it is the same as uninstalling this app.");
3159         pw.println("  trigger-recovery");
3160         pw.println("    Trigger Wi-Fi subsystem restart.");
3161         pw.println("  start-faking-scans");
3162         pw.println("    Start faking scan results into the framework (configured with "
3163                 + "'add-fake-scan'), stop with 'stop-faking-scans'.");
3164         pw.println("  stop-faking-scans");
3165         pw.println("    Stop faking scan results - started with 'start-faking-scans'.");
3166         pw.println("  add-fake-scan [-x] <ssid> <bssid> <capabilities> <frequency> <dbm>");
3167         pw.println("    Add a fake scan result to be used when enabled via `start-faking-scans'.");
3168         pw.println("    Example WPA2: add-fake-scan fakeWpa2 80:01:02:03:04:05 "
3169                 + "\"[WPA2-PSK-CCMP][RSN-PSK-CCMP][ESS]\" 2412 -55");
3170         pw.println("    Example WPA3: add-fake-scan fakeWpa3 80:01:02:03:04:06 "
3171                 + "\"[RSN-SAE+FT/SAE-CCMP][ESS]\" 2412 -55");
3172         pw.println(
3173                 "    Example Open: add-fake-scan fakeOpen 80:01:02:03:04:07 \"[ESS]\" 2412 -55");
3174         pw.println("    Example OWE: add-fake-scan fakeOwe 80:01:02:03:04:08 \"[RSN-OWE-CCMP]\" "
3175                 + "2412 -55");
3176         pw.println(
3177                 "    Example WPA2/WPA3 transition mode: add-fake-scan fakeWpa2t3 80:01:02:03:04:09 "
3178                         + "\"[WPA2-PSK-CCMP][RSN-PSK+SAE-CCMP][ESS][MFPC]\" 2412 -55");
3179         pw.println(
3180                 "    Example Open/OWE transition mode: add-fake-scan fakeOpenOwe 80:01:02:03:04:0A "
3181                         + "\"[RSN-OWE_TRANSITION-CCMP][ESS]\" 2412 -55");
3182         pw.println(
3183                 "    Example Passpoint: add-fake-scan fakePasspoint 80:01:02:03:04:0B "
3184                         + "\"[WPA2-EAP/SHA1-CCMP][RSN-EAP/SHA1-CCMP][ESS][MFPR][MFPC]"
3185                         + "[PASSPOINT]\" 2412 -55");
3186         pw.println("    -x - Specifies the SSID as hex digits instead of plain text");
3187         pw.println("  reset-fake-scans");
3188         pw.println("    Resets all fake scan results added by 'add-fake-scan'.");
3189         pw.println("  enable-scanning enabled|disabled [-h]");
3190         pw.println("    Sets whether all scanning should be enabled or disabled");
3191         pw.println("    -h - Enable scanning for hidden networks.");
3192         pw.println("  set-passpoint-enabled enabled|disabled");
3193         pw.println("    Sets whether Passpoint should be enabled or disabled");
3194         pw.println(
3195                 "  start-lohs <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition)"
3196                         + " <passphrase> [-b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6]"
3197                         + " [-x] [-w 20|40|80|160|320] [-f <int> [<int>]])");
3198         pw.println("    Start local only softap (hotspot) with provided params");
3199         pw.println("    <ssid> - SSID of the network");
3200         pw.println("    open|wpa2|wpa3|wpa3_transition|owe|owe_transition - Security type of the "
3201                 + "network.");
3202         pw.println("        - Use 'open', 'owe', 'owe_transition' for networks with no passphrase");
3203         pw.println("        - Use 'wpa2', 'wpa3', 'wpa3_transition' for networks with passphrase");
3204         pw.println("    -b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6 - select the "
3205                 + "preferred bands.");
3206         pw.println("        - Use '2' to select 2.4GHz band as the preferred band");
3207         pw.println("        - Use '5' to select 5GHz band as the preferred band");
3208         pw.println("        - Use '6' to select 6GHz band as the preferred band");
3209         pw.println("        - Use 'any' to indicate no band preference");
3210         pw.println("        - Use 'bridged' to indicate bridged AP which enables APs on both "
3211                 + "2.4G + 5G");
3212         pw.println("        - Use 'bridged_2_5' to indicate bridged AP which enables APs on both "
3213                 + "2.4G + 5G");
3214         pw.println("        - Use 'bridged_2_6' to indicate bridged AP which enables APs on both "
3215                 + "2.4G + 6G");
3216         pw.println("        - Use 'bridged_5_6' to indicate bridged AP which enables APs on both "
3217                 + "5G + 6G");
3218         pw.println("    Note: If the band option is not provided, 2.4GHz is the preferred band.");
3219         pw.println("          The exact channel is auto-selected by FW unless overridden by "
3220                 + "force-softap-channel command or '-f <int> <int>' option");
3221         pw.println("    -f <int> <int> - force exact channel frequency for operation channel");
3222         pw.println("    Note: -f <int> <int> - must be the last option");
3223         pw.println("          For example:");
3224         pw.println("          Use '-f 2412' to enable single Soft Ap on 2412");
3225         pw.println("          Use '-f 2412 5745' to enable bridged dual lohs on 2412 and 5745");
3226         pw.println("    -x - Specifies the SSID as hex digits instead of plain text (T and above)");
3227         pw.println("    -w 20|40|80|160|320 - select the maximum bandwidth (MHz)");
3228         pw.println("  stop-softap");
3229         pw.println("    Stop softap (hotspot)");
3230         pw.println("    Note: If the band option is not provided, 2.4GHz is the preferred band.");
3231         pw.println("  stop-lohs");
3232         pw.println("    Stop local only softap (hotspot)");
3233         pw.println("  set-multi-internet-mode 0|1|2");
3234         pw.println("    Sets Multi Internet use case mode. 0-disabled 1-dbs 2-multi ap");
3235         pw.println("  set-pno-request <ssid> [-f <frequency>]");
3236         pw.println("    Requests to include a non-quoted UTF-8 SSID in PNO scans");
3237         pw.println("  clear-pno-request");
3238         pw.println("    Clear the PNO scan request.");
3239         pw.println("  start-dpp-enrollee-responder [-i <info>] [-c <curve>]");
3240         pw.println("    Start DPP Enrollee responder mode.");
3241         pw.println("    -i - Device Info to be used in DPP Bootstrapping URI");
3242         pw.println("    -c - Cryptography Curve integer 1:p256v1, 2:s384r1, etc");
3243         pw.println("  start-dpp-configurator-initiator <networkId> <netRole> <enrolleeURI>");
3244         pw.println("    Start DPP Configurator Initiator mode.");
3245         pw.println("    netRole - 0: STA, 1: AP");
3246         pw.println("    enrolleeURI - Bootstrapping URI received from Enrollee");
3247         pw.println("  stop-dpp");
3248         pw.println("    Stop DPP session.");
3249         pw.println("  set-ssid-charset <locale_language> <charset_name>");
3250         pw.println("    Sets the SSID translation charset for the given locale language.");
3251         pw.println("    Example: set-ssid-charset zh GBK");
3252         pw.println("  clear-ssid-charsets");
3253         pw.println("    Clears the SSID translation charsets set in set-ssid-charset.");
3254         pw.println("  get-last-caller-info api_type");
3255         pw.println("    Get the last caller information for a WifiManager.ApiType");
3256         pw.println("  trigger-afc-location-update <longitude> <latitude> <height>");
3257         pw.println("    Passes in longitude, latitude, and height values as arguments of type "
3258                 + "double for a fake location update to trigger framework logic to query the AFC "
3259                 + "server. The longitude and latitude pair is in decimal degrees and the height"
3260                 + " is the altitude in meters. The server URL needs to have been previously set "
3261                 + "with the configure-afc-server shell command.");
3262         pw.println("    Example: trigger-afc-location-update 37.425056 -122.984157 3.043");
3263         pw.println("  set-afc-channel-allowance -e <secs_until_expiry> [-f <low_freq>,<high_freq>,"
3264                 + "<psd>:...|none] [-c <operating_class>,<channel_cfi>,<max_eirp>:...|none]");
3265         pw.println("    Sets the allowed AFC channels and frequencies.");
3266         pw.println("    -e - Seconds until the availability expires.");
3267         pw.println("    -f - Colon-separated list of available frequency info.");
3268         pw.println("      Note: each frequency should contain 3 comma separated values, where "
3269                 + "the first is the low frequency (MHz), the second the high frequency (MHz), the "
3270                 + "third the max PSD (dBm per MHz). To set an empty frequency list, enter \"none\" "
3271                 + "in place of the list of allowed frequencies.");
3272         pw.println("    -c - Colon-separated list of available channel info.");
3273         pw.println("      Note: each channel should contain 3 comma separated values, where "
3274                 + "the first is the global operating class, the second the channel CFI, "
3275                 + "the third the max EIRP in dBm. To set an empty channel list, enter \"none\" in "
3276                 + "place of the list of allowed channels.");
3277         pw.println("    Example: set-afc-channel-allowance -e 30 -c none -f "
3278                 + "5925,6020,23:6020,6050,1");
3279         pw.println("  configure-afc-server <url> [-r [<request property key> <request property "
3280                 + "value>] ...]");
3281         pw.println("    Sets the server URL and request properties for the HTTP request which the "
3282                 + "AFC Client will query.");
3283         pw.println("    -r - HTTP header fields that come in pairs of key and value which are added"
3284                 + " to the HTTP request. Must be an even number of arguments. If there is no -r "
3285                 + "option provided or no arguments provided after the -r option, then set the "
3286                 + "request properties to none in the request.");
3287         pw.println("    Example: configure-afc-server https://testURL -r key1 value1 key2 value2");
3288     }
3289 
3290     @Override
onHelp()3291     public void onHelp() {
3292         final PrintWriter pw = getOutPrintWriter();
3293         pw.println("Wi-Fi (wifi) commands:");
3294         pw.println("  help or -h");
3295         pw.println("    Print this help text.");
3296         onHelpNonPrivileged(pw);
3297         if (Binder.getCallingUid() == Process.ROOT_UID) {
3298             onHelpPrivileged(pw);
3299         }
3300         pw.println();
3301     }
3302 
printWifiNetworkSuggestions(PrintWriter pw, Collection<WifiNetworkSuggestion> suggestions)3303     private void printWifiNetworkSuggestions(PrintWriter pw,
3304             Collection<WifiNetworkSuggestion> suggestions) {
3305         if (suggestions == null || suggestions.isEmpty()) {
3306             pw.println("No suggestions on this device");
3307         } else {
3308             pw.println("SSID                         Security type(s)");
3309             for (WifiNetworkSuggestion suggestion : suggestions) {
3310                 pw.println(String.format("%-32s %-4s",
3311                         WifiInfo.sanitizeSsid(suggestion.getWifiConfiguration().SSID),
3312                         suggestion.getWifiConfiguration().getSecurityParamsList().stream()
3313                                 .map(p -> WifiConfiguration.getSecurityTypeName(
3314                                         p.getSecurityType())
3315                                         + (p.isAddedByAutoUpgrade() ? "^" : ""))
3316                                 .collect(Collectors.joining("/"))));
3317             }
3318         }
3319     }
3320 }
3321