• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
22 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
23 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
24 import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED;
25 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
26 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
27 
28 import static com.android.server.wifi.SelfRecovery.REASON_API_CALL;
29 
30 import android.content.BroadcastReceiver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.net.ConnectivityManager;
35 import android.net.MacAddress;
36 import android.net.Network;
37 import android.net.NetworkCapabilities;
38 import android.net.NetworkRequest;
39 import android.net.wifi.IActionListener;
40 import android.net.wifi.IScoreUpdateObserver;
41 import android.net.wifi.ISoftApCallback;
42 import android.net.wifi.IWifiConnectedNetworkScorer;
43 import android.net.wifi.ScanResult;
44 import android.net.wifi.SoftApCapability;
45 import android.net.wifi.SoftApConfiguration;
46 import android.net.wifi.SoftApInfo;
47 import android.net.wifi.SupplicantState;
48 import android.net.wifi.WifiClient;
49 import android.net.wifi.WifiConfiguration;
50 import android.net.wifi.WifiConnectedSessionInfo;
51 import android.net.wifi.WifiInfo;
52 import android.net.wifi.WifiManager;
53 import android.net.wifi.WifiNetworkSpecifier;
54 import android.net.wifi.WifiNetworkSuggestion;
55 import android.net.wifi.WifiScanner;
56 import android.net.wifi.WifiSsid;
57 import android.os.Binder;
58 import android.os.Process;
59 import android.os.RemoteException;
60 import android.os.SystemClock;
61 import android.telephony.Annotation;
62 import android.telephony.PhysicalChannelConfig;
63 import android.telephony.SubscriptionManager;
64 import android.telephony.TelephonyManager;
65 import android.text.TextUtils;
66 import android.util.Pair;
67 
68 import androidx.annotation.NonNull;
69 
70 import com.android.internal.annotations.VisibleForTesting;
71 import com.android.modules.utils.BasicShellCommandHandler;
72 import com.android.modules.utils.ParceledListSlice;
73 import com.android.modules.utils.build.SdkLevel;
74 import com.android.server.wifi.ClientMode.LinkProbeCallback;
75 import com.android.server.wifi.coex.CoexManager;
76 import com.android.server.wifi.coex.CoexUtils;
77 import com.android.server.wifi.hotspot2.NetworkDetail;
78 import com.android.server.wifi.util.ApConfigUtil;
79 import com.android.server.wifi.util.ArrayUtils;
80 import com.android.server.wifi.util.ScanResultUtil;
81 
82 import java.io.PrintWriter;
83 import java.nio.charset.StandardCharsets;
84 import java.util.ArrayList;
85 import java.util.Arrays;
86 import java.util.Collection;
87 import java.util.Collections;
88 import java.util.Comparator;
89 import java.util.List;
90 import java.util.Map;
91 import java.util.Set;
92 import java.util.concurrent.ArrayBlockingQueue;
93 import java.util.concurrent.ConcurrentHashMap;
94 import java.util.concurrent.CountDownLatch;
95 import java.util.concurrent.TimeUnit;
96 import java.util.stream.Collectors;
97 
98 /**
99  * Interprets and executes 'adb shell cmd wifi [args]'.
100  *
101  * To add new commands:
102  * - onCommand: Add a case "<command>" execute. Return a 0
103  *   if command executed successfully.
104  * - onHelp: add a description string.
105  *
106  * Permissions: currently root permission is required for some commands. Others will
107  * enforce the corresponding API permissions.
108  */
109 public class WifiShellCommand extends BasicShellCommandHandler {
110     @VisibleForTesting
111     public static String SHELL_PACKAGE_NAME = "com.android.shell";
112 
113     // These don't require root access.
114     // However, these do perform permission checks in the corresponding WifiService methods.
115     private static final String[] NON_PRIVILEGED_COMMANDS = {
116             "add-suggestion",
117             "add-network",
118             "connect-network",
119             "forget-network",
120             "get-country-code",
121             "help",
122             "-h",
123             "is-verbose-logging",
124             "list-scan-results",
125             "list-networks",
126             "list-suggestions",
127             "remove-suggestion",
128             "remove-all-suggestions",
129             "reset-connected-score",
130             "set-connected-score",
131             "set-scan-always-available",
132             "set-verbose-logging",
133             "set-wifi-enabled",
134             "start-scan",
135             "start-softap",
136             "status",
137             "stop-softap",
138     };
139 
140     private static final Map<String, Pair<NetworkRequest, ConnectivityManager.NetworkCallback>>
141             sActiveRequests = new ConcurrentHashMap<>();
142 
143     private final ActiveModeWarden mActiveModeWarden;
144     private final WifiGlobals mWifiGlobals;
145     private final WifiLockManager mWifiLockManager;
146     private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
147     private final WifiConfigManager mWifiConfigManager;
148     private final WifiNative mWifiNative;
149     private final CoexManager mCoexManager;
150     private final WifiCountryCode mWifiCountryCode;
151     private final WifiLastResortWatchdog mWifiLastResortWatchdog;
152     private final WifiServiceImpl mWifiService;
153     private final Context mContext;
154     private final ConnectivityManager mConnectivityManager;
155     private final WifiCarrierInfoManager mWifiCarrierInfoManager;
156     private final WifiNetworkFactory mWifiNetworkFactory;
157     private final SelfRecovery mSelfRecovery;
158     private final WifiThreadRunner mWifiThreadRunner;
159     private final WifiApConfigStore mWifiApConfigStore;
160     private int mSapState = WifiManager.WIFI_STATE_UNKNOWN;
161     private final ScanRequestProxy mScanRequestProxy;
162 
163     /**
164      * Used for shell command testing of scorer.
165      */
166     public static class WifiScorer extends IWifiConnectedNetworkScorer.Stub {
167         private final WifiServiceImpl mWifiService;
168         private final CountDownLatch mCountDownLatch;
169         private Integer mSessionId;
170         private IScoreUpdateObserver mScoreUpdateObserver;
171 
WifiScorer(WifiServiceImpl wifiService, CountDownLatch countDownLatch)172         public WifiScorer(WifiServiceImpl wifiService, CountDownLatch countDownLatch) {
173             mWifiService = wifiService;
174             mCountDownLatch  = countDownLatch;
175         }
176 
177         @Override
onStart(WifiConnectedSessionInfo sessionInfo)178         public void onStart(WifiConnectedSessionInfo sessionInfo) {
179             mSessionId = sessionInfo.getSessionId();
180             mCountDownLatch.countDown();
181         }
182         @Override
onStop(int sessionId)183         public void onStop(int sessionId) {
184             // clear the external scorer on disconnect.
185             mWifiService.clearWifiConnectedNetworkScorer();
186         }
187         @Override
onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl)188         public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) {
189             mScoreUpdateObserver = observerImpl;
190             mCountDownLatch.countDown();
191         }
192 
getSessionId()193         public Integer getSessionId() {
194             return mSessionId;
195         }
196 
getScoreUpdateObserver()197         public IScoreUpdateObserver getScoreUpdateObserver() {
198             return mScoreUpdateObserver;
199         }
200     }
201 
WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, Context context, WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner)202     WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, Context context,
203             WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner) {
204         mWifiGlobals = wifiGlobals;
205         mWifiThreadRunner = wifiThreadRunner;
206         mActiveModeWarden = wifiInjector.getActiveModeWarden();
207         mWifiLockManager = wifiInjector.getWifiLockManager();
208         mWifiNetworkSuggestionsManager = wifiInjector.getWifiNetworkSuggestionsManager();
209         mWifiConfigManager = wifiInjector.getWifiConfigManager();
210         mWifiNative = wifiInjector.getWifiNative();
211         mCoexManager = wifiInjector.getCoexManager();
212         mWifiCountryCode = wifiInjector.getWifiCountryCode();
213         mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
214         mWifiService = wifiService;
215         mContext = context;
216         mConnectivityManager = context.getSystemService(ConnectivityManager.class);
217         mWifiCarrierInfoManager = wifiInjector.getWifiCarrierInfoManager();
218         mWifiNetworkFactory = wifiInjector.getWifiNetworkFactory();
219         mSelfRecovery = wifiInjector.getSelfRecovery();
220         mWifiApConfigStore = wifiInjector.getWifiApConfigStore();
221         mScanRequestProxy = wifiInjector.getScanRequestProxy();
222     }
223 
224     @Override
onCommand(String cmd)225     public int onCommand(String cmd) {
226         // Treat no command as help command.
227         if (cmd == null || cmd.equals("")) {
228             cmd = "help";
229         }
230         // Explicit exclusion from root permission
231         if (ArrayUtils.indexOf(NON_PRIVILEGED_COMMANDS, cmd) == -1) {
232             final int uid = Binder.getCallingUid();
233             if (uid != Process.ROOT_UID) {
234                 throw new SecurityException(
235                         "Uid " + uid + " does not have access to " + cmd + " wifi command "
236                                 + "(or such command doesn't exist)");
237             }
238         }
239 
240         final PrintWriter pw = getOutPrintWriter();
241         try {
242             switch (cmd) {
243                 case "set-ipreach-disconnect": {
244                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
245                     mWifiGlobals.setIpReachabilityDisconnectEnabled(enabled);
246                     return 0;
247                 }
248                 case "get-ipreach-disconnect":
249                     pw.println("IPREACH_DISCONNECT state is "
250                             + mWifiGlobals.getIpReachabilityDisconnectEnabled());
251                     return 0;
252                 case "set-poll-rssi-interval-msecs":
253                     int newPollIntervalMsecs;
254                     try {
255                         newPollIntervalMsecs = Integer.parseInt(getNextArgRequired());
256                     } catch (NumberFormatException e) {
257                         pw.println(
258                                 "Invalid argument to 'set-poll-rssi-interval-msecs' "
259                                         + "- must be a positive integer");
260                         return -1;
261                     }
262 
263                     if (newPollIntervalMsecs < 1) {
264                         pw.println(
265                                 "Invalid argument to 'set-poll-rssi-interval-msecs' "
266                                         + "- must be a positive integer");
267                         return -1;
268                     }
269 
270                     mWifiGlobals.setPollRssiIntervalMillis(newPollIntervalMsecs);
271                     return 0;
272                 case "get-poll-rssi-interval-msecs":
273                     pw.println("WifiGlobals.getPollRssiIntervalMillis() = "
274                             + mWifiGlobals.getPollRssiIntervalMillis());
275                     return 0;
276                 case "force-hi-perf-mode": {
277                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
278                     if (!mWifiLockManager.forceHiPerfMode(enabled)) {
279                         pw.println("Command execution failed");
280                     }
281                     return 0;
282                 }
283                 case "force-low-latency-mode": {
284                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
285                     if (!mWifiLockManager.forceLowLatencyMode(enabled)) {
286                         pw.println("Command execution failed");
287                     }
288                     return 0;
289                 }
290                 case "network-suggestions-set-user-approved": {
291                     String packageName = getNextArgRequired();
292                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
293                     mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(approved,
294                             Binder.getCallingUid(), packageName);
295                     return 0;
296                 }
297                 case "network-suggestions-has-user-approved": {
298                     String packageName = getNextArgRequired();
299                     boolean hasUserApproved =
300                             mWifiNetworkSuggestionsManager.hasUserApprovedForApp(packageName);
301                     pw.println(hasUserApproved ? "yes" : "no");
302                     return 0;
303                 }
304                 case "imsi-protection-exemption-set-user-approved-for-carrier": {
305                     String arg1 = getNextArgRequired();
306                     int carrierId = -1;
307                     try {
308                         carrierId = Integer.parseInt(arg1);
309                     } catch (NumberFormatException e) {
310                         pw.println("Invalid argument to "
311                                 + "'imsi-protection-exemption-set-user-approved-for-carrier' "
312                                 + "- carrierId must be an Integer");
313                         return -1;
314                     }
315                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
316                     mWifiCarrierInfoManager
317                             .setHasUserApprovedImsiPrivacyExemptionForCarrier(approved, carrierId);
318                     return 0;
319                 }
320                 case "imsi-protection-exemption-has-user-approved-for-carrier": {
321                     String arg1 = getNextArgRequired();
322                     int carrierId = -1;
323                     try {
324                         carrierId = Integer.parseInt(arg1);
325                     } catch (NumberFormatException e) {
326                         pw.println("Invalid argument to "
327                                 + "'imsi-protection-exemption-has-user-approved-for-carrier' "
328                                 + "- 'carrierId' must be an Integer");
329                         return -1;
330                     }
331                     boolean hasUserApproved = mWifiCarrierInfoManager
332                             .hasUserApprovedImsiPrivacyExemptionForCarrier(carrierId);
333                     pw.println(hasUserApproved ? "yes" : "no");
334                     return 0;
335                 }
336                 case "imsi-protection-exemption-clear-user-approved-for-carrier": {
337                     String arg1 = getNextArgRequired();
338                     int carrierId = -1;
339                     try {
340                         carrierId = Integer.parseInt(arg1);
341                     } catch (NumberFormatException e) {
342                         pw.println("Invalid argument to "
343                                 + "'imsi-protection-exemption-clear-user-approved-for-carrier' "
344                                 + "- 'carrierId' must be an Integer");
345                         return -1;
346                     }
347                     mWifiCarrierInfoManager.clearImsiPrivacyExemptionForCarrier(carrierId);
348                     return 0;
349                 }
350                 case "network-requests-remove-user-approved-access-points": {
351                     String packageName = getNextArgRequired();
352                     mWifiNetworkFactory.removeUserApprovedAccessPointsForApp(packageName);
353                     return 0;
354                 }
355                 case "clear-user-disabled-networks": {
356                     mWifiConfigManager.clearUserTemporarilyDisabledList();
357                     return 0;
358                 }
359                 case "send-link-probe": {
360                     return sendLinkProbe(pw);
361                 }
362                 case "force-softap-band": {
363                     boolean forceBandEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
364                     if (forceBandEnabled) {
365                         String forcedBand = getNextArgRequired();
366                         if (forcedBand.equals("2")) {
367                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
368                                     SoftApConfiguration.BAND_2GHZ, 0);
369                         } else if (forcedBand.equals("5")) {
370                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
371                                     SoftApConfiguration.BAND_5GHZ, 0);
372                         } else if (forcedBand.equals("6")) {
373                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
374                                     SoftApConfiguration.BAND_6GHZ, 0);
375                         } else {
376                             pw.println("Invalid argument to 'force-softap-band enabled' "
377                                     + "- must be a valid band integer (2|5|6)");
378                             return -1;
379                         }
380                         return 0;
381                     } else {
382                         mWifiApConfigStore.disableForceSoftApBandOrChannel();
383                         return 0;
384                     }
385 
386                 }
387                 case "force-softap-channel": {
388                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
389                     if (enabled) {
390                         int apChannelMHz;
391                         try {
392                             apChannelMHz = Integer.parseInt(getNextArgRequired());
393                         } catch (NumberFormatException e) {
394                             pw.println("Invalid argument to 'force-softap-channel enabled' "
395                                     + "- must be a positive integer");
396                             return -1;
397                         }
398                         int apChannel = ScanResult.convertFrequencyMhzToChannelIfSupported(
399                                 apChannelMHz);
400                         int band = ApConfigUtil.convertFrequencyToBand(apChannelMHz);
401                         pw.println("channel: " + apChannel + " band: " + band);
402                         if (apChannel == -1 || band == -1) {
403                             pw.println("Invalid argument to 'force-softap-channel enabled' "
404                                     + "- must be a valid WLAN channel");
405                             return -1;
406                         }
407                         boolean isTemporarilyEnablingWifiNeeded = mWifiService.getWifiEnabledState()
408                                 != WIFI_STATE_ENABLED;
409                         if (isTemporarilyEnablingWifiNeeded) {
410                             waitForWifiEnabled(true);
411                         }
412                         // Following calls will fail if wifi is not enabled
413                         boolean isValidChannel = isApChannelMHzValid(pw, apChannelMHz);
414                         if (isTemporarilyEnablingWifiNeeded) {
415                             waitForWifiEnabled(false);
416                         }
417                         if (!isValidChannel
418                                 || (band == SoftApConfiguration.BAND_5GHZ
419                                 && !mWifiService.is5GHzBandSupported())
420                                 || (band == SoftApConfiguration.BAND_6GHZ
421                                 && !mWifiService.is6GHzBandSupported())
422                                 || (band == SoftApConfiguration.BAND_60GHZ
423                                 && !mWifiService.is60GHzBandSupported())) {
424                             pw.println("Invalid argument to 'force-softap-channel enabled' "
425                                     + "- must be a valid WLAN channel"
426                                     + " in a band supported by the device");
427                             return -1;
428                         }
429                         mWifiApConfigStore.enableForceSoftApBandOrChannel(band, apChannel);
430                         return 0;
431                     } else {
432                         mWifiApConfigStore.disableForceSoftApBandOrChannel();
433                         return 0;
434                     }
435                 }
436                 case "start-softap": {
437                     CountDownLatch countDownLatch = new CountDownLatch(1);
438                     ISoftApCallback.Stub softApCallback = new ISoftApCallback.Stub() {
439                         @Override
440                         public void onStateChanged(int state, int failureReason) {
441                             pw.println("onStateChanged with state: " + state
442                                     + " failure reason: " + failureReason);
443                             mSapState = state;
444                             if (state == WifiManager.WIFI_AP_STATE_ENABLED) {
445                                 pw.println(" SAP is enabled successfully");
446                                 // Skip countDown() and wait for onInfoChanged() which has
447                                 // the confirmed softAp channel information
448                             } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
449                                 pw.println(" SAP is disabled");
450                             } else if (state == WifiManager.WIFI_AP_STATE_FAILED) {
451                                 pw.println(" SAP failed to start");
452                                 countDownLatch.countDown();
453                             }
454                         }
455 
456                         @Override
457                         public void onConnectedClientsOrInfoChanged(Map<String, SoftApInfo> infos,
458                                 Map<String, List<WifiClient>> clients, boolean isBridged,
459                                 boolean isRegistration) {
460                             if (mSapState == WifiManager.WIFI_AP_STATE_ENABLED) {
461                                 countDownLatch.countDown();
462                             }
463                         }
464 
465                         @Override
466                         public void onCapabilityChanged(SoftApCapability capability) {
467                             pw.println("onCapabilityChanged " + capability);
468                         }
469 
470                         @Override
471                         public void onBlockedClientConnecting(WifiClient client, int reason) {
472                         }
473 
474                     };
475                     mWifiService.registerSoftApCallback(softApCallback);
476                     SoftApConfiguration config = buildSoftApConfiguration(pw);
477                     if (!mWifiService.startTetheredHotspot(config, SHELL_PACKAGE_NAME)) {
478                         pw.println("Soft AP failed to start. Please check config parameters");
479                     }
480                     // Wait for softap to start and complete callback
481                     countDownLatch.await(3000, TimeUnit.MILLISECONDS);
482                     mWifiService.unregisterSoftApCallback(softApCallback);
483                     return 0;
484                 }
485                 case "stop-softap": {
486                     if (mWifiService.stopSoftAp()) {
487                         pw.println("Soft AP stopped successfully");
488                     } else {
489                         pw.println("Soft AP failed to stop");
490                     }
491                     return 0;
492                 }
493                 case "force-country-code": {
494                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
495                     if (enabled) {
496                         String countryCode = getNextArgRequired();
497                         if (!WifiCountryCode.isValid(countryCode)) {
498                             pw.println("Invalid argument: Country code must be a 2-Character"
499                                     + " alphanumeric code. But got countryCode " + countryCode
500                                     + " instead");
501                             return -1;
502                         }
503                         mWifiCountryCode.setOverrideCountryCode(countryCode);
504                         return 0;
505                     } else {
506                         mWifiCountryCode.clearOverrideCountryCode();
507                         return 0;
508                     }
509                 }
510                 case "get-country-code": {
511                     pw.println("Wifi Country Code = "
512                             + mWifiCountryCode.getCountryCode());
513                     return 0;
514                 }
515                 case "set-wifi-watchdog": {
516                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
517                     mWifiLastResortWatchdog.setWifiWatchdogFeature(enabled);
518                     return 0;
519                 }
520                 case "get-wifi-watchdog": {
521                     pw.println("wifi watchdog state is "
522                             + mWifiLastResortWatchdog.getWifiWatchdogFeature());
523                     return 0;
524                 }
525                 case "set-wifi-enabled": {
526                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
527                     mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled);
528                     return 0;
529                 }
530                 case "set-scan-always-available": {
531                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
532                     mWifiService.setScanAlwaysAvailable(enabled, SHELL_PACKAGE_NAME);
533                     return 0;
534                 }
535                 case "get-softap-supported-features":
536                     // This command is used for vts to check softap supported features.
537                     if (ApConfigUtil.isAcsSupported(mContext)) {
538                         pw.println("wifi_softap_acs_supported");
539                     }
540                     if (ApConfigUtil.isWpa3SaeSupported(mContext)) {
541                         pw.println("wifi_softap_wpa3_sae_supported");
542                     }
543                     if ((mWifiService.getSupportedFeatures()
544                             & WifiManager.WIFI_FEATURE_BRIDGED_AP)
545                             == WifiManager.WIFI_FEATURE_BRIDGED_AP) {
546                         pw.println("wifi_softap_bridged_ap_supported");
547                     }
548                     if ((mWifiService.getSupportedFeatures()
549                             & WifiManager.WIFI_FEATURE_STA_BRIDGED_AP)
550                             == WifiManager.WIFI_FEATURE_STA_BRIDGED_AP) {
551                         pw.println("wifi_softap_bridged_ap_with_sta_supported");
552                     }
553                     return 0;
554                 case "settings-reset":
555                     mWifiNative.stopFakingScanDetails();
556                     mWifiNative.resetFakeScanDetails();
557                     mWifiService.factoryReset(SHELL_PACKAGE_NAME);
558                     return 0;
559                 case "list-scan-results":
560                     List<ScanResult> scanResults =
561                             mWifiService.getScanResults(SHELL_PACKAGE_NAME, null);
562                     if (scanResults.isEmpty()) {
563                         pw.println("No scan results");
564                     } else {
565                         ScanResultUtil.dumpScanResults(pw, scanResults,
566                                 SystemClock.elapsedRealtime());
567                     }
568                     return 0;
569                 case "start-scan":
570                     mWifiService.startScan(SHELL_PACKAGE_NAME, null);
571                     return 0;
572                 case "list-networks":
573                     ParceledListSlice<WifiConfiguration> networks =
574                             mWifiService.getConfiguredNetworks(SHELL_PACKAGE_NAME, null, false);
575                     if (networks == null || networks.getList().isEmpty()) {
576                         pw.println("No networks");
577                     } else {
578                         pw.println("Network Id      SSID                         Security type");
579                         for (WifiConfiguration network : networks.getList()) {
580                             String securityType = network.getSecurityParamsList().stream()
581                                     .map(p -> WifiConfiguration.getSecurityTypeName(
582                                                     p.getSecurityType())
583                                             + (p.isAddedByAutoUpgrade() ? "^" : ""))
584                                     .collect(Collectors.joining("/"));
585                             pw.println(String.format("%-12d %-32s %-4s",
586                                     network.networkId, WifiInfo.sanitizeSsid(network.SSID),
587                                     securityType));
588                         }
589                     }
590                     return 0;
591                 case "connect-network": {
592                     CountDownLatch countDownLatch = new CountDownLatch(1);
593                     IActionListener.Stub actionListener = new IActionListener.Stub() {
594                         @Override
595                         public void onSuccess() throws RemoteException {
596                             pw.println("Connection initiated ");
597                             countDownLatch.countDown();
598                         }
599 
600                         @Override
601                         public void onFailure(int i) throws RemoteException {
602                             pw.println("Connection failed");
603                             countDownLatch.countDown();
604                         }
605                     };
606                     WifiConfiguration config = buildWifiConfiguration(pw);
607                     mWifiService.connect(config, -1, actionListener);
608                     // wait for status.
609                     countDownLatch.await(500, TimeUnit.MILLISECONDS);
610                     setAutoJoin(pw, config.SSID, config.allowAutojoin);
611                     return 0;
612                 }
613                 case "add-network": {
614                     CountDownLatch countDownLatch = new CountDownLatch(1);
615                     IActionListener.Stub actionListener = new IActionListener.Stub() {
616                         @Override
617                         public void onSuccess() throws RemoteException {
618                             pw.println("Save successful");
619                             countDownLatch.countDown();
620                         }
621 
622                         @Override
623                         public void onFailure(int i) throws RemoteException {
624                             pw.println("Save failed");
625                             countDownLatch.countDown();
626                         }
627                     };
628                     WifiConfiguration config = buildWifiConfiguration(pw);
629                     mWifiService.save(config, actionListener);
630                     // wait for status.
631                     countDownLatch.await(500, TimeUnit.MILLISECONDS);
632                     setAutoJoin(pw, config.SSID, config.allowAutojoin);
633                     return 0;
634                 }
635                 case "forget-network": {
636                     String networkId = getNextArgRequired();
637                     CountDownLatch countDownLatch = new CountDownLatch(1);
638                     IActionListener.Stub actionListener = new IActionListener.Stub() {
639                         @Override
640                         public void onSuccess() throws RemoteException {
641                             pw.println("Forget successful");
642                             countDownLatch.countDown();
643                         }
644 
645                         @Override
646                         public void onFailure(int i) throws RemoteException {
647                             pw.println("Forget failed");
648                             countDownLatch.countDown();
649                         }
650                     };
651                     mWifiService.forget(Integer.parseInt(networkId), actionListener);
652                     // wait for status.
653                     countDownLatch.await(500, TimeUnit.MILLISECONDS);
654                     return 0;
655                 }
656                 case "pmksa-flush": {
657                     String networkId = getNextArgRequired();
658                     int netId = Integer.parseInt(networkId);
659                     WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(netId);
660                     if (config == null) {
661                         pw.println("No Wifi config corresponding to networkId: " + netId);
662                         return -1;
663                     }
664                     mWifiNative.removeNetworkCachedData(netId);
665                     return 0;
666                 }
667                 case "status":
668                     printStatus(pw);
669                     return 0;
670                 case "set-verbose-logging": {
671                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
672                     mWifiService.enableVerboseLogging(enabled ? 1 : 0);
673                     return 0;
674                 }
675                 case "is-verbose-logging": {
676                     int enabled = mWifiService.getVerboseLoggingLevel();
677                     pw.println(enabled > 0 ? "enabled" : "disabled");
678                     return 0;
679                 }
680                 case "start-restricting-auto-join-to-subscription-id": {
681                     if (!SdkLevel.isAtLeastS()) {
682                         pw.println("This feature is only supported on SdkLevel S or later.");
683                         return -1;
684                     }
685                     int subId = Integer.parseInt(getNextArgRequired());
686                     mWifiService.startRestrictingAutoJoinToSubscriptionId(subId);
687                     return 0;
688                 }
689                 case "stop-restricting-auto-join-to-subscription-id": {
690                     if (!SdkLevel.isAtLeastS()) {
691                         pw.println("This feature is only supported on SdkLevel S or later.");
692                         return -1;
693                     }
694                     mWifiService.stopRestrictingAutoJoinToSubscriptionId();
695                     return 0;
696                 }
697                 case "add-suggestion": {
698                     WifiNetworkSuggestion suggestion = buildSuggestion(pw);
699                     if (suggestion  == null) {
700                         pw.println("Invalid network suggestion parameter");
701                         return -1;
702                     }
703                     int errorCode = mWifiService.addNetworkSuggestions(
704                             Arrays.asList(suggestion), SHELL_PACKAGE_NAME, null);
705                     if (errorCode != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
706                         pw.println("Add network suggestion failed with error code: " + errorCode);
707                         return -1;
708                     }
709                     // untrusted/oem-paid networks need a corresponding NetworkRequest.
710                     if (suggestion.isUntrusted()
711                             || (SdkLevel.isAtLeastS()
712                             && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
713                         NetworkRequest.Builder networkRequestBuilder =
714                                 new NetworkRequest.Builder()
715                                         .addTransportType(TRANSPORT_WIFI);
716                         if (suggestion.isUntrusted()) {
717                             networkRequestBuilder.removeCapability(NET_CAPABILITY_TRUSTED);
718                         }
719                         if (SdkLevel.isAtLeastS()) {
720                             if (suggestion.isOemPaid()) {
721                                 networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PAID);
722                             }
723                             if (suggestion.isOemPrivate()) {
724                                 networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PRIVATE);
725                             }
726                         }
727                         NetworkRequest networkRequest = networkRequestBuilder.build();
728                         ConnectivityManager.NetworkCallback networkCallback =
729                                 new ConnectivityManager.NetworkCallback();
730                         pw.println("Adding request: " + networkRequest);
731                         mConnectivityManager.requestNetwork(networkRequest, networkCallback);
732                         sActiveRequests.put(
733                                 suggestion.getSsid(), Pair.create(networkRequest, networkCallback));
734                     }
735                     return 0;
736                 }
737                 case "remove-suggestion": {
738                     String ssid = getNextArgRequired();
739                     List<WifiNetworkSuggestion> suggestions =
740                             mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
741                     WifiNetworkSuggestion suggestion = suggestions.stream()
742                             .filter(s -> s.getSsid().equals(ssid))
743                             .findAny()
744                             .orElse(null);
745                     if (suggestion == null) {
746                         pw.println("No matching suggestion to remove");
747                         return -1;
748                     }
749                     mWifiService.removeNetworkSuggestions(
750                             Arrays.asList(suggestion), SHELL_PACKAGE_NAME);
751                     // untrusted/oem-paid networks need a corresponding NetworkRequest.
752                     if (suggestion.isUntrusted()
753                             || (SdkLevel.isAtLeastS()
754                             && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
755                         Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
756                                 sActiveRequests.remove(suggestion.getSsid());
757                         if (nrAndNc == null) {
758                             pw.println("No matching request to remove");
759                             return -1;
760                         }
761                         pw.println("Removing request: " + nrAndNc.first);
762                         mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
763                     }
764                     return 0;
765                 }
766                 case "remove-all-suggestions":
767                     mWifiService.removeNetworkSuggestions(
768                             Collections.emptyList(), SHELL_PACKAGE_NAME);
769                     return 0;
770                 case "list-suggestions": {
771                     List<WifiNetworkSuggestion> suggestions =
772                             mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
773                     printWifiNetworkSuggestions(pw, suggestions);
774                     return 0;
775                 }
776                 case "list-all-suggestions": {
777                     Set<WifiNetworkSuggestion> suggestions =
778                             mWifiNetworkSuggestionsManager.getAllNetworkSuggestions();
779                     printWifiNetworkSuggestions(pw, suggestions);
780                     return 0;
781                 }
782                 case "list-suggestions-from-app": {
783                     String packageName = getNextArgRequired();
784                     List<WifiNetworkSuggestion> suggestions =
785                             mWifiService.getNetworkSuggestions(packageName);
786                     printWifiNetworkSuggestions(pw, suggestions);
787                     return 0;
788                 }
789                 case "add-request": {
790                     NetworkRequest networkRequest = buildNetworkRequest(pw);
791                     ConnectivityManager.NetworkCallback networkCallback =
792                             new ConnectivityManager.NetworkCallback();
793                     pw.println("Adding request: " + networkRequest);
794                     mConnectivityManager.requestNetwork(networkRequest, networkCallback);
795                     String ssid = getAllArgs()[1];
796                     sActiveRequests.put(ssid, Pair.create(networkRequest, networkCallback));
797                     return 0;
798                 }
799                 case "remove-request": {
800                     String ssid = getNextArgRequired();
801                     Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
802                             sActiveRequests.remove(ssid);
803                     if (nrAndNc == null) {
804                         pw.println("No matching request to remove");
805                         return -1;
806                     }
807                     pw.println("Removing request: " + nrAndNc.first);
808                     mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
809                     return 0;
810                 }
811                 case "remove-all-requests":
812                     if (sActiveRequests.isEmpty()) {
813                         pw.println("No active requests");
814                         return -1;
815                     }
816                     for (Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc
817                             : sActiveRequests.values()) {
818                         pw.println("Removing request: " + nrAndNc.first);
819                         mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
820                     }
821                     sActiveRequests.clear();
822                     return 0;
823                 case "list-requests":
824                     if (sActiveRequests.isEmpty()) {
825                         pw.println("No active requests");
826                     } else {
827                         pw.println("SSID                         NetworkRequest");
828                         for (Map.Entry<String,
829                                 Pair<NetworkRequest, ConnectivityManager.NetworkCallback>> entry :
830                                 sActiveRequests.entrySet()) {
831                             pw.println(String.format("%-32s %-4s",
832                                     entry.getKey(), entry.getValue().first));
833                         }
834                     }
835                     return 0;
836                 case "network-requests-set-user-approved": {
837                     String packageName = getNextArgRequired();
838                     boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
839                     mWifiNetworkFactory.setUserApprovedApp(packageName, approved);
840                     return 0;
841                 }
842                 case "network-requests-has-user-approved": {
843                     String packageName = getNextArgRequired();
844                     boolean hasUserApproved = mWifiNetworkFactory.hasUserApprovedApp(packageName);
845                     pw.println(hasUserApproved ? "yes" : "no");
846                     return 0;
847                 }
848                 case "set-coex-cell-channels": {
849                     if (!SdkLevel.isAtLeastS()) {
850                         return handleDefaultCommands(cmd);
851                     }
852                     mCoexManager.setMockCellChannels(buildCoexCellChannels());
853                     return 0;
854                 }
855                 case "reset-coex-cell-channels": {
856                     if (!SdkLevel.isAtLeastS()) {
857                         return handleDefaultCommands(cmd);
858                     }
859                     mCoexManager.resetMockCellChannels();
860                     return 0;
861                 }
862                 case "get-coex-cell-channels": {
863                     if (!SdkLevel.isAtLeastS()) {
864                         return handleDefaultCommands(cmd);
865                     }
866                     pw.println("Cell channels: " + mCoexManager.getCellChannels());
867                     return 0;
868                 }
869                 case "set-connected-score": {
870                     int score = Integer.parseInt(getNextArgRequired());
871                     CountDownLatch countDownLatch = new CountDownLatch(2);
872                     mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer
873                     WifiScorer connectedScorer = new WifiScorer(mWifiService, countDownLatch);
874                     if (mWifiService.setWifiConnectedNetworkScorer(new Binder(), connectedScorer)) {
875                         // wait for retrieving the session id & score observer.
876                         countDownLatch.await(1000, TimeUnit.MILLISECONDS);
877                     }
878                     if (connectedScorer.getSessionId() == null
879                             || connectedScorer.getScoreUpdateObserver() == null) {
880                         pw.println("Did not receive session id and/or the score update observer. "
881                                 + "Is the device connected to a wifi network?");
882                         mWifiService.clearWifiConnectedNetworkScorer();
883                         return -1;
884                     }
885                     pw.println("Updating score: " + score + " for session id: "
886                             + connectedScorer.getSessionId());
887                     try {
888                         connectedScorer.getScoreUpdateObserver().notifyScoreUpdate(
889                                 connectedScorer.getSessionId(), score);
890                     } catch (RemoteException e) {
891                         pw.println("Failed to send the score update");
892                         mWifiService.clearWifiConnectedNetworkScorer();
893                         return -1;
894                     }
895                     return 0;
896                 }
897                 case "reset-connected-score": {
898                     mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer
899                     return 0;
900                 }
901                 case "network-suggestions-set-as-carrier-provider": {
902                     String packageName = getNextArgRequired();
903                     boolean enabled = getNextArgRequiredTrueOrFalse("yes", "no");
904                     mWifiNetworkSuggestionsManager
905                             .setAppWorkingAsCrossCarrierProvider(packageName, enabled);
906                     return 0;
907                 }
908                 case "is-network-suggestions-set-as-carrier-provider": {
909                     String packageName = getNextArgRequired();
910                     pw.println(mWifiNetworkSuggestionsManager
911                             .isAppWorkingAsCrossCarrierProvider(packageName) ? "yes" : "no");
912                     return 0;
913                 }
914                 case "remove-shell-app-from-suggestion_database <packageName>": {
915                     String packageName = getNextArgRequired();
916                     mWifiNetworkSuggestionsManager.removeApp(packageName);
917                     return 0;
918                 }
919                 case "set-emergency-callback-mode": {
920                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
921                     mActiveModeWarden.emergencyCallbackModeChanged(enabled);
922                     return 0;
923                 }
924                 case "set-emergency-call-state": {
925                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
926                     mActiveModeWarden.emergencyCallStateChanged(enabled);
927                     return 0;
928                 }
929                 case "trigger-recovery": {
930                     mSelfRecovery.trigger(REASON_API_CALL);
931                     return 0;
932                 }
933                 case "add-fake-scan": {
934                     String ssid = getNextArgRequired();
935                     String bssid = getNextArgRequired();
936                     String capabilities = getNextArgRequired();
937                     int frequency;
938                     int dbm;
939                     String freqStr = getNextArgRequired();
940                     try {
941                         frequency = Integer.parseInt(freqStr);
942                     } catch (NumberFormatException e) {
943                         pw.println(
944                                 "Invalid frequency argument to 'add-fake-scan' "
945                                         + "- must be an integer: " + freqStr);
946                         return -1;
947                     }
948                     if (frequency <= 0) {
949                         pw.println("Invalid frequency argument to 'add-fake-scan' - must be a "
950                                 + "positive integer: " + freqStr);
951                     }
952                     String dbmString = getNextArgRequired();
953                     try {
954                         dbm = Integer.parseInt(dbmString);
955                     } catch (NumberFormatException e) {
956                         pw.println(
957                                 "Invalid dbm argument to 'add-fake-scan' "
958                                         + "- must be an integer: " + dbmString);
959                         return -1;
960                     }
961                     ScanResult.InformationElement ieSSid = new ScanResult.InformationElement(
962                             ScanResult.InformationElement.EID_SSID,
963                             0,
964                             ssid.getBytes(StandardCharsets.UTF_8));
965                     ScanResult.InformationElement[] ies =
966                             new ScanResult.InformationElement[]{ieSSid};
967                     ScanDetail sd = new ScanDetail(new NetworkDetail(bssid, ies, null, frequency),
968                             WifiSsid.createFromAsciiEncoded(ssid), bssid, capabilities, dbm,
969                             frequency, SystemClock.elapsedRealtime() * 1000, ies, null, null);
970                     mWifiNative.addFakeScanDetail(sd);
971                     return 0;
972                 }
973                 case "reset-fake-scans":
974                     mWifiNative.resetFakeScanDetails();
975                     return 0;
976                 case "start-faking-scans":
977                     mWifiNative.startFakingScanDetails();
978                     mWifiService.startScan(SHELL_PACKAGE_NAME, null); // to trigger update
979                     return 0;
980                 case "stop-faking-scans":
981                     mWifiNative.stopFakingScanDetails();
982                     return 0;
983                 case "enable-scanning":
984                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
985                     boolean hiddenEnabled = false;
986                     String option = getNextOption();
987                     if (option != null) {
988                         if (option.equals("-h")) {
989                             hiddenEnabled = true;
990                         } else {
991                             pw.println("Invalid argument to 'enable-scanning' "
992                                     + "- only allowed option is '-h'");
993                             return -1;
994                         }
995                     }
996                     mScanRequestProxy.enableScanning(enabled, hiddenEnabled);
997                     return 0;
998                 default:
999                     return handleDefaultCommands(cmd);
1000             }
1001         } catch (IllegalArgumentException e) {
1002             pw.println("Invalid args for " + cmd + ": " + e);
1003             return -1;
1004         } catch (Exception e) {
1005             pw.println("Exception while executing WifiShellCommand: ");
1006             e.printStackTrace(pw);
1007             return -1;
1008         }
1009     }
1010 
getNextArgRequiredTrueOrFalse(String trueString, String falseString)1011     private boolean getNextArgRequiredTrueOrFalse(String trueString, String falseString)
1012             throws IllegalArgumentException {
1013         String nextArg = getNextArgRequired();
1014         if (trueString.equals(nextArg)) {
1015             return true;
1016         } else if (falseString.equals(nextArg)) {
1017             return false;
1018         } else {
1019             throw new IllegalArgumentException("Expected '" + trueString + "' or '" + falseString
1020                     + "' as next arg but got '" + nextArg + "'");
1021         }
1022     }
1023 
buildWifiConfiguration(PrintWriter pw)1024     private WifiConfiguration buildWifiConfiguration(PrintWriter pw) {
1025         String ssid = getNextArgRequired();
1026         String type = getNextArgRequired();
1027         WifiConfiguration configuration = new WifiConfiguration();
1028         configuration.SSID = "\"" + ssid + "\"";
1029         if (TextUtils.equals(type, "wpa3")) {
1030             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
1031             configuration.preSharedKey = "\"" + getNextArgRequired() + "\"";
1032         } else if (TextUtils.equals(type, "wpa2")) {
1033             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
1034             configuration.preSharedKey = "\"" + getNextArgRequired() + "\"";
1035         } else if (TextUtils.equals(type, "owe")) {
1036             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
1037         } else if (TextUtils.equals(type, "open")) {
1038             configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
1039         } else {
1040             throw new IllegalArgumentException("Unknown network type " + type);
1041         }
1042         String option = getNextOption();
1043         while (option != null) {
1044             if (option.equals("-m")) {
1045                 configuration.meteredOverride = METERED_OVERRIDE_METERED;
1046             } else if (option.equals("-d")) {
1047                 configuration.allowAutojoin = false;
1048             } else if (option.equals("-b")) {
1049                 configuration.BSSID = getNextArgRequired();
1050             } else if (option.equals("-r")) {
1051                 String macRandomizationScheme = getNextArgRequired();
1052                 if (macRandomizationScheme.equals("auto")) {
1053                     configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO;
1054                 } else if (macRandomizationScheme.equals("none")) {
1055                     configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
1056                 } else if (macRandomizationScheme.equals("persistent")) {
1057                     configuration.macRandomizationSetting =
1058                             WifiConfiguration.RANDOMIZATION_PERSISTENT;
1059                 } else if (macRandomizationScheme.equals("non_persistent")) {
1060                     if (SdkLevel.isAtLeastS()) {
1061                         configuration.macRandomizationSetting =
1062                                 WifiConfiguration.RANDOMIZATION_NON_PERSISTENT;
1063                     } else {
1064                         throw new IllegalArgumentException(
1065                                 "-r non_persistent MAC randomization not supported before S");
1066                     }
1067                 }
1068             } else if (option.equals("-h")) {
1069                 configuration.hiddenSSID = true;
1070             } else if (option.equals("-p")) {
1071                 configuration.shared = false;
1072             } else {
1073                 pw.println("Ignoring unknown option " + option);
1074             }
1075             option = getNextOption();
1076         }
1077         return configuration;
1078     }
1079 
buildSoftApConfiguration(PrintWriter pw)1080     private SoftApConfiguration buildSoftApConfiguration(PrintWriter pw) {
1081         String ssid = getNextArgRequired();
1082         String type = getNextArgRequired();
1083         SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
1084         configBuilder.setSsid(ssid);
1085         if (TextUtils.equals(type, "wpa2")) {
1086             configBuilder.setPassphrase(getNextArgRequired(),
1087                     SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
1088         } else if (TextUtils.equals(type, "wpa3")) {
1089             configBuilder.setPassphrase(getNextArgRequired(),
1090                     SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
1091         } else if (TextUtils.equals(type, "wpa3_transition")) {
1092             configBuilder.setPassphrase(getNextArgRequired(),
1093                     SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
1094         } else if (TextUtils.equals(type, "open")) {
1095             configBuilder.setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN);
1096         } else {
1097             throw new IllegalArgumentException("Unknown network type " + type);
1098         }
1099         String option = getNextOption();
1100         while (option != null) {
1101             if (option.equals("-b")) {
1102                 String preferredBand = getNextArgRequired();
1103                 if (preferredBand.equals("2")) {
1104                     configBuilder.setBand(SoftApConfiguration.BAND_2GHZ);
1105                 } else if (preferredBand.equals("5")) {
1106                     configBuilder.setBand(SoftApConfiguration.BAND_5GHZ);
1107                 } else if (preferredBand.equals("6")) {
1108                     configBuilder.setBand(SoftApConfiguration.BAND_6GHZ);
1109                 } else if (preferredBand.equals("any")) {
1110                     configBuilder.setBand(SoftApConfiguration.BAND_2GHZ
1111                             | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ);
1112                 } else if (preferredBand.equals("bridged")) {
1113                     if (SdkLevel.isAtLeastS()) {
1114                         int[] dualBands = new int[] {
1115                                 SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ};
1116                         configBuilder.setBands(dualBands);
1117                     } else {
1118                         throw new IllegalArgumentException(
1119                                 "-b bridged option is not supported before S");
1120                     }
1121                 } else {
1122                     throw new IllegalArgumentException("Invalid band option " + preferredBand);
1123                 }
1124             } else {
1125                 pw.println("Ignoring unknown option " + option);
1126             }
1127             option = getNextOption();
1128         }
1129         return configBuilder.build();
1130     }
1131 
buildSuggestion(PrintWriter pw)1132     private WifiNetworkSuggestion buildSuggestion(PrintWriter pw) {
1133         String ssid = getNextArgRequired();
1134         String type = getNextArgRequired();
1135         WifiNetworkSuggestion.Builder suggestionBuilder =
1136                 new WifiNetworkSuggestion.Builder();
1137         suggestionBuilder.setSsid(ssid);
1138         if (TextUtils.equals(type, "wpa3")) {
1139             suggestionBuilder.setWpa3Passphrase(getNextArgRequired());
1140         } else if (TextUtils.equals(type, "wpa2")) {
1141             suggestionBuilder.setWpa2Passphrase(getNextArgRequired());
1142         } else if (TextUtils.equals(type, "owe")) {
1143             suggestionBuilder.setIsEnhancedOpen(true);
1144         } else if (TextUtils.equals(type, "open")) {
1145             // nothing to do.
1146         } else {
1147             throw new IllegalArgumentException("Unknown network type " + type);
1148         }
1149         boolean isCarrierMerged = false;
1150         String option = getNextOption();
1151         while (option != null) {
1152             if (option.equals("-u")) {
1153                 suggestionBuilder.setUntrusted(true);
1154             } else if (option.equals("-o")) {
1155                 if (SdkLevel.isAtLeastS()) {
1156                     suggestionBuilder.setOemPaid(true);
1157                 } else {
1158                     throw new IllegalArgumentException(
1159                             "-o OEM paid suggestions not supported before S");
1160                 }
1161             } else if (option.equals("-p")) {
1162                 if (SdkLevel.isAtLeastS()) {
1163                     suggestionBuilder.setOemPrivate(true);
1164                 } else {
1165                     throw new IllegalArgumentException(
1166                             "-p OEM private suggestions not supported before S");
1167                 }
1168             } else if (option.equals("-m")) {
1169                 suggestionBuilder.setIsMetered(true);
1170             } else if (option.equals("-s")) {
1171                 suggestionBuilder.setCredentialSharedWithUser(true);
1172             } else if (option.equals("-d")) {
1173                 suggestionBuilder.setIsInitialAutojoinEnabled(false);
1174             } else if (option.equals("-b")) {
1175                 suggestionBuilder.setBssid(MacAddress.fromString(getNextArgRequired()));
1176             } else if (option.equals("-r")) {
1177                 if (SdkLevel.isAtLeastS()) {
1178                     suggestionBuilder.setMacRandomizationSetting(
1179                             WifiNetworkSuggestion.RANDOMIZATION_NON_PERSISTENT);
1180                 } else {
1181                     throw new IllegalArgumentException(
1182                             "-r non_persistent MAC randomization not supported before S");
1183                 }
1184             } else if (option.equals("-a")) {
1185                 if (SdkLevel.isAtLeastS()) {
1186                     isCarrierMerged = true;
1187                 } else {
1188                     throw new IllegalArgumentException("-a option is not supported before S");
1189                 }
1190             } else if (option.equals("-i")) {
1191                 if (SdkLevel.isAtLeastS()) {
1192                     int subId = Integer.parseInt(getNextArgRequired());
1193                     suggestionBuilder.setSubscriptionId(subId);
1194                 } else {
1195                     throw new IllegalArgumentException(
1196                             "-i subscription ID option is not supported before S");
1197                 }
1198             } else if (option.equals("-c")) {
1199                 int carrierId = Integer.parseInt(getNextArgRequired());
1200                 suggestionBuilder.setCarrierId(carrierId);
1201             } else if (option.equals("-h")) {
1202                 suggestionBuilder.setIsHiddenSsid(true);
1203             } else {
1204                 pw.println("Ignoring unknown option " + option);
1205             }
1206             option = getNextOption();
1207         }
1208         WifiNetworkSuggestion suggestion = suggestionBuilder.build();
1209         if (isCarrierMerged) {
1210             if (suggestion.wifiConfiguration.subscriptionId
1211                     == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1212                 pw.println("Carrier merged network must have valid subscription Id");
1213                 return null;
1214             }
1215             suggestion.wifiConfiguration.carrierMerged = true;
1216         }
1217         return suggestion;
1218     }
1219 
buildNetworkRequest(PrintWriter pw)1220     private NetworkRequest buildNetworkRequest(PrintWriter pw) {
1221         String ssid = getNextArgRequired();
1222         String type = getNextArgRequired();
1223         WifiNetworkSpecifier.Builder specifierBuilder =
1224                 new WifiNetworkSpecifier.Builder();
1225         specifierBuilder.setSsid(ssid);
1226         if (TextUtils.equals(type, "wpa3")) {
1227             specifierBuilder.setWpa3Passphrase(getNextArgRequired());
1228         } else if (TextUtils.equals(type, "wpa3_transition")) {
1229             specifierBuilder.setWpa3Passphrase(getNextArgRequired());
1230         } else if (TextUtils.equals(type, "wpa2")) {
1231             specifierBuilder.setWpa2Passphrase(getNextArgRequired());
1232         } else if (TextUtils.equals(type, "owe")) {
1233             specifierBuilder.setIsEnhancedOpen(true);
1234         } else if (TextUtils.equals(type, "open")) {
1235             // nothing to do.
1236         } else {
1237             throw new IllegalArgumentException("Unknown network type " + type);
1238         }
1239         String bssid = null;
1240         String option = getNextOption();
1241         while (option != null) {
1242             if (option.equals("-b")) {
1243                 bssid = getNextArgRequired();
1244             } else {
1245                 pw.println("Ignoring unknown option " + option);
1246             }
1247             option = getNextOption();
1248         }
1249 
1250         // Permission approval bypass is only available to requests with both ssid & bssid set.
1251         // So, find scan result with the best rssi level to set in the request.
1252         if (bssid == null) {
1253             ScanResult matchingScanResult =
1254                     mWifiService.getScanResults(SHELL_PACKAGE_NAME, null)
1255                             .stream()
1256                             .filter(s -> s.SSID.equals(ssid))
1257                             .max(Comparator.comparingInt(s -> s.level))
1258                             .orElse(null);
1259             if (matchingScanResult != null) {
1260                 bssid = matchingScanResult.BSSID;
1261             } else {
1262                 pw.println("No matching bssid found, request will need UI approval");
1263             }
1264         }
1265         if (bssid != null) specifierBuilder.setBssid(MacAddress.fromString(bssid));
1266         return new NetworkRequest.Builder()
1267                 .addTransportType(TRANSPORT_WIFI)
1268                 .removeCapability(NET_CAPABILITY_INTERNET)
1269                 .setNetworkSpecifier(specifierBuilder.build())
1270                 .build();
1271     }
1272 
1273     @NonNull
buildCoexCellChannels()1274     private List<CoexUtils.CoexCellChannel> buildCoexCellChannels() {
1275         List<CoexUtils.CoexCellChannel> cellChannels = new ArrayList<>();
1276         while (getRemainingArgsCount() > 0) {
1277             final @Annotation.NetworkType int rat;
1278             final String ratArg = getNextArgRequired();
1279             if (TextUtils.equals(ratArg, "lte")) {
1280                 rat = TelephonyManager.NETWORK_TYPE_LTE;
1281             } else if (TextUtils.equals(ratArg, "nr")) {
1282                 rat = TelephonyManager.NETWORK_TYPE_NR;
1283             } else {
1284                 throw new IllegalArgumentException("Unknown rat type " + ratArg);
1285             }
1286             final int band = Integer.parseInt(getNextArgRequired());
1287             if (band < 1 || band > 261) {
1288                 throw new IllegalArgumentException("Band is " + band
1289                         + " but should be a value from 1 to 261");
1290             }
1291             final int downlinkFreqKhz = Integer.parseInt(getNextArgRequired());
1292             if (downlinkFreqKhz < 0 && downlinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) {
1293                 throw new IllegalArgumentException("Downlink frequency is " + downlinkFreqKhz
1294                         + " but should be >= 0 or UNKNOWN: "
1295                         + PhysicalChannelConfig.FREQUENCY_UNKNOWN);
1296             }
1297             final int downlinkBandwidthKhz = Integer.parseInt(getNextArgRequired());
1298             if (downlinkBandwidthKhz <= 0
1299                     && downlinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
1300                 throw new IllegalArgumentException("Downlink bandwidth is " + downlinkBandwidthKhz
1301                         + " but should be > 0 or UNKNOWN: "
1302                         + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN);
1303             }
1304             final int uplinkFreqKhz = Integer.parseInt(getNextArgRequired());
1305             if (uplinkFreqKhz < 0 && uplinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) {
1306                 throw new IllegalArgumentException("Uplink frequency is " + uplinkFreqKhz
1307                         + " but should be >= 0 or UNKNOWN: "
1308                         + PhysicalChannelConfig.FREQUENCY_UNKNOWN);
1309             }
1310             final int uplinkBandwidthKhz = Integer.parseInt(getNextArgRequired());
1311             if (uplinkBandwidthKhz <= 0
1312                     && uplinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
1313                 throw new IllegalArgumentException("Uplink bandwidth is " + uplinkBandwidthKhz
1314                         + " but should be > 0 or UNKNOWN: "
1315                         + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN);
1316             }
1317             cellChannels.add(new CoexUtils.CoexCellChannel(rat, band,
1318                     downlinkFreqKhz, downlinkBandwidthKhz, uplinkFreqKhz, uplinkBandwidthKhz,
1319                     SubscriptionManager.INVALID_SUBSCRIPTION_ID));
1320         }
1321         return cellChannels;
1322     }
1323 
setAutoJoin(PrintWriter pw, String ssid, boolean allowAutojoin)1324     private void setAutoJoin(PrintWriter pw, String ssid, boolean allowAutojoin) {
1325         // For suggestions, this will work only if the config has already been added
1326         // to WifiConfigManager.
1327         WifiConfiguration retrievedConfig =
1328                 mWifiService.getPrivilegedConfiguredNetworks(SHELL_PACKAGE_NAME, null)
1329                         .getList()
1330                         .stream()
1331                         .filter(n -> n.SSID.equals(ssid))
1332                         .findAny()
1333                         .orElse(null);
1334         if (retrievedConfig == null) {
1335             pw.println("Cannot retrieve config, autojoin setting skipped.");
1336             return;
1337         }
1338         mWifiService.allowAutojoin(retrievedConfig.networkId, allowAutojoin);
1339     }
1340 
sendLinkProbe(PrintWriter pw)1341     private int sendLinkProbe(PrintWriter pw) throws InterruptedException {
1342         // Note: should match WifiNl80211Manager#SEND_MGMT_FRAME_TIMEOUT_MS
1343         final int sendMgmtFrameTimeoutMs = 1000;
1344 
1345         ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
1346         mWifiThreadRunner.post(() ->
1347                 mActiveModeWarden.getPrimaryClientModeManager().probeLink(new LinkProbeCallback() {
1348                     @Override
1349                     public void onAck(int elapsedTimeMs) {
1350                         queue.offer("Link probe succeeded after " + elapsedTimeMs + " ms");
1351                     }
1352 
1353                     @Override
1354                     public void onFailure(int reason) {
1355                         queue.offer("Link probe failed with reason "
1356                                 + LinkProbeCallback.failureReasonToString(reason));
1357                     }
1358                 }, -1));
1359 
1360         // block until msg is received, or timed out
1361         String msg = queue.poll(sendMgmtFrameTimeoutMs + 1000, TimeUnit.MILLISECONDS);
1362         if (msg == null) {
1363             pw.println("Link probe timed out");
1364         } else {
1365             pw.println(msg);
1366         }
1367         return 0;
1368     }
1369 
isApChannelMHzValid(PrintWriter pw, int apChannelMHz)1370     private boolean isApChannelMHzValid(PrintWriter pw, int apChannelMHz) {
1371         int[] allowed2gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
1372         int[] allowed5gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
1373         int[] allowed5gDfsFreq =
1374             mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
1375         int[] allowed6gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ);
1376         int[] allowed60gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_60_GHZ);
1377         if (allowed2gFreq == null) {
1378             allowed2gFreq = new int[0];
1379         }
1380         if (allowed5gFreq == null) {
1381             allowed5gFreq = new int[0];
1382         }
1383         if (allowed5gDfsFreq == null) {
1384             allowed5gDfsFreq = new int[0];
1385         }
1386         if (allowed6gFreq == null) {
1387             allowed6gFreq = new int[0];
1388         }
1389         if (allowed60gFreq == null) {
1390             allowed60gFreq = new int[0];
1391         }
1392         pw.println("2G freq: " + Arrays.toString(allowed2gFreq));
1393         pw.println("5G freq: " + Arrays.toString(allowed5gFreq));
1394         pw.println("5G DFS: " + Arrays.toString(allowed5gDfsFreq));
1395         pw.println("6G freq: " + Arrays.toString(allowed6gFreq));
1396         pw.println("60G freq: " + Arrays.toString(allowed60gFreq));
1397         return (Arrays.binarySearch(allowed2gFreq, apChannelMHz) >= 0
1398                 || Arrays.binarySearch(allowed5gFreq, apChannelMHz) >= 0
1399                 || Arrays.binarySearch(allowed5gDfsFreq, apChannelMHz) >= 0)
1400                 || Arrays.binarySearch(allowed6gFreq, apChannelMHz) >= 0
1401                 || Arrays.binarySearch(allowed60gFreq, apChannelMHz) >= 0;
1402     }
1403 
waitForWifiEnabled(boolean enabled)1404     private void waitForWifiEnabled(boolean enabled) throws InterruptedException {
1405         CountDownLatch countDownLatch = new CountDownLatch(1);
1406         BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
1407             @Override
1408             public void onReceive(Context context, Intent intent) {
1409                 String action = intent.getAction();
1410                 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
1411                     int state = mWifiService.getWifiEnabledState();
1412                     if ((enabled && state == WIFI_STATE_ENABLED)
1413                             || (!enabled && state == WIFI_STATE_DISABLED)) {
1414                         countDownLatch.countDown();
1415                     }
1416                 }
1417             }
1418         };
1419         IntentFilter filter = new IntentFilter();
1420         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
1421         mContext.registerReceiver(broadcastReceiver, filter);
1422         mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled);
1423         countDownLatch.await(5000, TimeUnit.MILLISECONDS);
1424         mContext.unregisterReceiver(broadcastReceiver);
1425     }
1426 
printWifiInfo(PrintWriter pw, WifiInfo info)1427     private void printWifiInfo(PrintWriter pw, WifiInfo info) {
1428         if (info.getSupplicantState() != SupplicantState.COMPLETED) {
1429             pw.println("Wifi is not connected");
1430             return;
1431         }
1432         pw.println("Wifi is connected to " + info.getSSID());
1433         pw.println("WifiInfo: " + info);
1434         // additional diagnostics not printed by WifiInfo.toString()
1435         pw.println("successfulTxPackets: " + info.txSuccess);
1436         pw.println("successfulTxPacketsPerSecond: " + info.getSuccessfulTxPacketsPerSecond());
1437         pw.println("retriedTxPackets: " + info.txRetries);
1438         pw.println("retriedTxPacketsPerSecond: " + info.getRetriedTxPacketsPerSecond());
1439         pw.println("lostTxPackets: " + info.txBad);
1440         pw.println("lostTxPacketsPerSecond: " + info.getLostTxPacketsPerSecond());
1441         pw.println("successfulRxPackets: " + info.rxSuccess);
1442         pw.println("successfulRxPacketsPerSecond: " + info.getSuccessfulRxPacketsPerSecond());
1443     }
1444 
printStatus(PrintWriter pw)1445     private void printStatus(PrintWriter pw) {
1446         boolean wifiEnabled = mWifiService.getWifiEnabledState() == WIFI_STATE_ENABLED;
1447         pw.println("Wifi is " + (wifiEnabled ? "enabled" : "disabled"));
1448         pw.println("Wifi scanning is "
1449                 + (mWifiService.isScanAlwaysAvailable()
1450                 ? "always available" : "only available when wifi is enabled"));
1451         if (!wifiEnabled) {
1452             return;
1453         }
1454         if (Binder.getCallingUid() != Process.ROOT_UID) {
1455             // not privileged, just dump the primary client mode manager manager status
1456             // (public API contents).
1457             pw.println("==== Primary ClientModeManager instance ====");
1458             printWifiInfo(pw, mWifiService.getConnectionInfo(SHELL_PACKAGE_NAME, null));
1459         } else {
1460             // privileged, dump out all the client mode manager manager statuses
1461             for (ClientModeManager cm : mActiveModeWarden.getClientModeManagers()) {
1462                 pw.println("==== ClientModeManager instance: " + cm + " ====");
1463                 WifiInfo info = cm.syncRequestConnectionInfo();
1464                 printWifiInfo(pw, info);
1465                 if (info.getSupplicantState() != SupplicantState.COMPLETED) {
1466                     continue;
1467                 }
1468                 Network network = cm.syncGetCurrentNetwork();
1469                 NetworkCapabilities capabilities =
1470                         mConnectivityManager.getNetworkCapabilities(network);
1471                 pw.println("NetworkCapabilities: " + capabilities);
1472             }
1473         }
1474     }
1475 
onHelpNonPrivileged(PrintWriter pw)1476     private void onHelpNonPrivileged(PrintWriter pw) {
1477         pw.println("  get-country-code");
1478         pw.println("    Gets country code as a two-letter string");
1479         pw.println("  set-wifi-enabled enabled|disabled");
1480         pw.println("    Enables/disables Wifi on this device.");
1481         pw.println("  set-scan-always-available enabled|disabled");
1482         pw.println("    Sets whether scanning should be available even when wifi is off.");
1483         pw.println("  list-scan-results");
1484         pw.println("    Lists the latest scan results");
1485         pw.println("  start-scan");
1486         pw.println("    Start a new scan");
1487         pw.println("  list-networks");
1488         pw.println("    Lists the saved networks");
1489         pw.println("  connect-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-m] [-d] "
1490                 + "[-b <bssid>] [-r auto|none|persistent|non_persistent]");
1491         pw.println("    Connect to a network with provided params and add to saved networks list");
1492         pw.println("    <ssid> - SSID of the network");
1493         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
1494         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
1495         pw.println("           - 'open' - Open networks (Most prevalent)");
1496         pw.println("           - 'owe' - Enhanced open networks");
1497         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
1498         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
1499         pw.println("           - 'wpa3' - WPA-3 PSK networks");
1500         pw.println("    -m - Mark the network metered.");
1501         pw.println("    -d - Mark the network autojoin disabled.");
1502         pw.println("    -h - Mark the network hidden.");
1503         pw.println("    -p - Mark the network private (not shared).");
1504         pw.println("    -b <bssid> - Set specific BSSID.");
1505         pw.println("    -r auto|none|persistent|non_persistent - MAC randomization scheme for the"
1506                 + " network");
1507         pw.println("  add-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-m] [-d] "
1508                 + "[-b <bssid>] [-r auto|none|persistent|non_persistent]");
1509         pw.println("    Add/update saved network with provided params");
1510         pw.println("    <ssid> - SSID of the network");
1511         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
1512         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
1513         pw.println("           - 'open' - Open networks (Most prevalent)");
1514         pw.println("           - 'owe' - Enhanced open networks");
1515         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
1516         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
1517         pw.println("           - 'wpa3' - WPA-3 PSK networks");
1518         pw.println("    -m - Mark the network metered.");
1519         pw.println("    -d - Mark the network autojoin disabled.");
1520         pw.println("    -h - Mark the network hidden.");
1521         pw.println("    -p - Mark the network private (not shared).");
1522         pw.println("    -b <bssid> - Set specific BSSID.");
1523         pw.println("    -r auto|none|persistent|non_persistent - MAC randomization scheme for the"
1524                 + " network");
1525         pw.println("  forget-network <networkId>");
1526         pw.println("    Remove the network mentioned by <networkId>");
1527         pw.println("        - Use list-networks to retrieve <networkId> for the network");
1528         pw.println("  status");
1529         pw.println("    Current wifi status");
1530         pw.println("  set-verbose-logging enabled|disabled ");
1531         pw.println("    Set the verbose logging enabled or disabled");
1532         pw.println("  is-verbose-logging");
1533         pw.println("    Check whether verbose logging enabled or disabled");
1534         pw.println("  start-restricting-auto-join-to-subscription-id subId");
1535         pw.println("    temporarily disable all wifi networks except merged carrier networks with"
1536                 + " the given subId");
1537         pw.println("  stop-restricting-auto-join-to-subscription-id");
1538         pw.println("    Undo the effects of "
1539                 + "start-restricting-auto-join-to-subscription-id");
1540         pw.println("  add-suggestion <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-u] [-o] [-p] [-m] "
1541                 + " [-s] [-d] [-b <bssid>] [-e] [-i] [-a <carrierId>] [-c <subscriptionId>]");
1542         pw.println("    Add a network suggestion with provided params");
1543         pw.println("    Use 'network-suggestions-set-user-approved " + SHELL_PACKAGE_NAME + " yes'"
1544                 +  " to approve suggestions added via shell (Needs root access)");
1545         pw.println("    <ssid> - SSID of the network");
1546         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
1547         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
1548         pw.println("           - 'open' - Open networks (Most prevalent)");
1549         pw.println("           - 'owe' - Enhanced open networks");
1550         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
1551         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
1552         pw.println("           - 'wpa3' - WPA-3 PSK networks");
1553         pw.println("    -u - Mark the suggestion untrusted.");
1554         pw.println("    -o - Mark the suggestion oem paid.");
1555         pw.println("    -p - Mark the suggestion oem private.");
1556         pw.println("    -m - Mark the suggestion metered.");
1557         pw.println("    -h - Mark the network hidden.");
1558         pw.println("    -s - Share the suggestion with user.");
1559         pw.println("    -d - Mark the suggestion autojoin disabled.");
1560         pw.println("    -b <bssid> - Set specific BSSID.");
1561         pw.println("    -r - Enable non_persistent randomization (disabled by default)");
1562         pw.println("    -a - Mark the suggestion carrier merged");
1563         pw.println("    -c <carrierId> - set carrier Id");
1564         pw.println("    -i <subscriptionId> - set subscription Id, if -a is used, "
1565                 + "this must be set");
1566         pw.println("  remove-suggestion <ssid>");
1567         pw.println("    Remove a network suggestion with provided SSID of the network");
1568         pw.println("  remove-all-suggestions");
1569         pw.println("    Removes all suggestions added via shell");
1570         pw.println("  list-suggestions");
1571         pw.println("    Lists the suggested networks added via shell");
1572         if (SdkLevel.isAtLeastS()) {
1573             pw.println("  set-coex-cell-channels [lte|nr <bandNumber 1-261> "
1574                     + "<downlinkFreqKhz or UNKNOWN: "
1575                     + PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> "
1576                     + "<downlinkBandwidthKhz or UNKNOWN: "
1577                     + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + "> "
1578                     + "<uplinkFreqKhz or UNKNOWN: "
1579                     + PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> "
1580                     + "<uplinkBandwidthKhz or UNKNOWN: "
1581                     + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + ">] ...");
1582             pw.println("    Sets a list of zero or more cell channels to use for coex calculations."
1583                     + " Actual device reported cell channels will be ignored until"
1584                     + " reset-coex-cell-channels is called.");
1585             pw.println("  reset-coex-cell-channels");
1586             pw.println("    Removes all cell channels set in set-coex-cell-channels and returns to "
1587                     + "listening on actual device reported cell channels");
1588             pw.println("  get-coex-cell-channels");
1589             pw.println("    Prints the cell channels being used for coex.");
1590         }
1591         pw.println("  set-connected-score <score>");
1592         pw.println("    Set connected wifi network score (to choose between LTE & Wifi for "
1593                 + "default route).");
1594         pw.println("    This turns off the active connected scorer (default or external).");
1595         pw.println("    Only works while connected to a wifi network. This score will stay in "
1596                 + "effect until you call reset-connected-score or the device disconnects from the "
1597                 + "current network.");
1598         pw.println("    <score> - Integer score should be in the range of 0 - 60");
1599         pw.println("  reset-connected-score");
1600         pw.println("    Turns on the default connected scorer.");
1601         pw.println("    Note: Will clear any external scorer set.");
1602         pw.println("  start-softap <ssid> (open|wpa2|wpa3|wpa3_transition) <passphrase> "
1603                 + "[-b 2|5|6|any]");
1604         pw.println("    Start softap with provided params");
1605         pw.println("    Note that the shell command doesn't activate internet tethering. In some "
1606                 + "devices, internet sharing is possible when Wi-Fi STA is also enabled and is"
1607                 + "associated to another AP with internet access.");
1608         pw.println("    <ssid> - SSID of the network");
1609         pw.println("    open|wpa2|wpa3|wpa3_transition - Security type of the network.");
1610         pw.println("        - Use 'open' for networks with no passphrase");
1611         pw.println("        - Use 'wpa2', 'wpa3', 'wpa3_transition' for networks with passphrase");
1612         pw.println("    -b 2|5|6|any|bridged - select the preferred band.");
1613         pw.println("        - Use '2' to select 2.4GHz band as the preferred band");
1614         pw.println("        - Use '5' to select 5GHz band as the preferred band");
1615         pw.println("        - Use '6' to select 6GHz band as the preferred band");
1616         pw.println("        - Use 'any' to indicate no band preference");
1617         pw.println("        - Use 'bridged' to indicate bridged AP which enables APs on both "
1618                 + "2.4G + 5G");
1619         pw.println("    Note: If the band option is not provided, 2.4GHz is the preferred band.");
1620         pw.println("          The exact channel is auto-selected by FW unless overridden by "
1621                 + "force-softap-channel command");
1622         pw.println("  stop-softap");
1623         pw.println("    Stop softap (hotspot)");
1624         pw.println("  pmksa-flush <networkId>");
1625         pw.println("        - Flush the local PMKSA cache associated with the network id."
1626                 + " Use list-networks to retrieve <networkId> for the network");
1627     }
1628 
onHelpPrivileged(PrintWriter pw)1629     private void onHelpPrivileged(PrintWriter pw) {
1630         pw.println("  set-ipreach-disconnect enabled|disabled");
1631         pw.println("    Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.");
1632         pw.println("  get-ipreach-disconnect");
1633         pw.println("    Gets setting of CMD_IP_REACHABILITY_LOST events triggering disconnects.");
1634         pw.println("  set-poll-rssi-interval-msecs <int>");
1635         pw.println("    Sets the interval between RSSI polls to <int> milliseconds.");
1636         pw.println("  get-poll-rssi-interval-msecs");
1637         pw.println("    Gets current interval between RSSI polls, in milliseconds.");
1638         pw.println("  force-hi-perf-mode enabled|disabled");
1639         pw.println("    Sets whether hi-perf mode is forced or left for normal operation.");
1640         pw.println("  force-low-latency-mode enabled|disabled");
1641         pw.println("    Sets whether low latency mode is forced or left for normal operation.");
1642         pw.println("  network-suggestions-set-user-approved <package name> yes|no");
1643         pw.println("    Sets whether network suggestions from the app is approved or not.");
1644         pw.println("  network-suggestions-has-user-approved <package name>");
1645         pw.println("    Queries whether network suggestions from the app is approved or not.");
1646         pw.println("  imsi-protection-exemption-set-user-approved-for-carrier <carrier id> yes|no");
1647         pw.println("    Sets whether Imsi protection exemption for carrier is approved or not");
1648         pw.println("  imsi-protection-exemption-has-user-approved-for-carrier <carrier id>");
1649         pw.println("    Queries whether Imsi protection exemption for carrier is approved or not");
1650         pw.println("  imsi-protection-exemption-clear-user-approved-for-carrier <carrier id>");
1651         pw.println("    Clear the user choice on Imsi protection exemption for carrier");
1652         pw.println("  network-requests-remove-user-approved-access-points <package name>");
1653         pw.println("    Removes all user approved network requests for the app.");
1654         pw.println("  clear-user-disabled-networks");
1655         pw.println("    Clears the user disabled networks list.");
1656         pw.println("  send-link-probe");
1657         pw.println("    Manually triggers a link probe.");
1658         pw.println("  force-softap-band enabled <int> | disabled");
1659         pw.println("    Forces soft AP band to 2|5|6");
1660         pw.println("  force-softap-channel enabled <int> | disabled");
1661         pw.println("    Sets whether soft AP channel is forced to <int> MHz");
1662         pw.println("    or left for normal   operation.");
1663         pw.println("  force-country-code enabled <two-letter code> | disabled ");
1664         pw.println("    Sets country code to <two-letter code> or left for normal value");
1665         pw.println("  set-wifi-watchdog enabled|disabled");
1666         pw.println("    Sets whether wifi watchdog should trigger recovery");
1667         pw.println("  get-wifi-watchdog");
1668         pw.println("    Gets setting of wifi watchdog trigger recovery.");
1669         pw.println("  get-softap-supported-features");
1670         pw.println("    Gets softap supported features. Will print 'wifi_softap_acs_supported'");
1671         pw.println("    and/or 'wifi_softap_wpa3_sae_supported',");
1672         pw.println("    and/or 'wifi_softap_bridged_ap_supported',");
1673         pw.println("    and/or 'wifi_softap_bridged_ap_with_sta_supported',");
1674         pw.println("    each on a separate line.");
1675         pw.println("  settings-reset");
1676         pw.println("    Initiates wifi settings reset");
1677         pw.println("  add-request <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-b <bssid>]");
1678         pw.println("    Add a network request with provided params");
1679         pw.println("    Use 'network-requests-set-user-approved android yes'"
1680                 +  " to pre-approve requests added via rooted shell (Not persisted)");
1681         pw.println("    <ssid> - SSID of the network");
1682         pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
1683         pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
1684         pw.println("           - 'open' - Open networks (Most prevalent)");
1685         pw.println("           - 'owe' - Enhanced open networks");
1686         pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
1687         pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
1688         pw.println("           - 'wpa3' - WPA-3 PSK networks");
1689         pw.println("    -b <bssid> - Set specific BSSID.");
1690         pw.println("  remove-request <ssid>");
1691         pw.println("    Remove a network request with provided SSID of the network");
1692         pw.println("  remove-all-requests");
1693         pw.println("    Removes all active requests added via shell");
1694         pw.println("  list-requests");
1695         pw.println("    Lists the requested networks added via shell");
1696         pw.println("  network-requests-set-user-approved <package name> yes|no");
1697         pw.println("    Sets whether network requests from the app is approved or not.");
1698         pw.println("    Note: Only 1 such app can be approved from the shell at a time");
1699         pw.println("  network-requests-has-user-approved <package name>");
1700         pw.println("    Queries whether network requests from the app is approved or not.");
1701         pw.println("    Note: This only returns whether the app was set via the "
1702                 + "'network-requests-set-user-approved' shell command");
1703         pw.println("  list-all-suggestions");
1704         pw.println("    Lists all suggested networks on this device");
1705         pw.println("  list-suggestions-from-app <package name>");
1706         pw.println("    Lists the suggested networks from the app");
1707         pw.println("  set-emergency-callback-mode enabled|disabled");
1708         pw.println("    Sets whether Emergency Callback Mode (ECBM) is enabled.");
1709         pw.println("    Equivalent to receiving the "
1710                 + "TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED broadcast.");
1711         pw.println("  set-emergency-call-state enabled|disabled");
1712         pw.println("    Sets whether we are in the middle of an emergency call.");
1713         pw.println("Equivalent to receiving the "
1714                 + "TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED broadcast.");
1715         pw.println("  network-suggestions-set-as-carrier-provider <packageName> yes|no");
1716         pw.println("    Set the <packageName> work as carrier provider or not.");
1717         pw.println("  is-network-suggestions-set-as-carrier-provider <packageName>");
1718         pw.println("    Queries whether the <packageName> is working as carrier provider or not.");
1719         pw.println("  remove-app-from-suggestion_database <packageName>");
1720         pw.println("    Remove <packageName> from the suggestion database, all suggestions and user"
1721                 + " approval will be deleted, it is the same as uninstalling this app.");
1722         pw.println("  trigger-recovery");
1723         pw.println("    Trigger Wi-Fi subsystem restart.");
1724         pw.println("  start-faking-scans");
1725         pw.println("    Start faking scan results into the framework (configured with "
1726                 + "'add-fake-scan'), stop with 'stop-faking-scans'.");
1727         pw.println("  stop-faking-scans");
1728         pw.println("    Stop faking scan results - started with 'start-faking-scans'.");
1729         pw.println("  add-fake-scan <ssid> <bssid> <capabilities> <frequency> <dbm>");
1730         pw.println("    Add a fake scan result to be used when enabled via `start-faking-scans'.");
1731         pw.println("    Example WPA2: add-fake-scan fakeWpa2 80:01:02:03:04:05 "
1732                 + "\"[WPA2-PSK-CCMP][RSN-PSK-CCMP][ESS]\" 2412 -55");
1733         pw.println("    Example WPA3: add-fake-scan fakeWpa3 80:01:02:03:04:06 "
1734                 + "\"[RSN-SAE+FT/SAE-CCMP][ESS]\" 2412 -55");
1735         pw.println(
1736                 "    Example Open: add-fake-scan fakeOpen 80:01:02:03:04:07 \"[ESS]\" 2412 -55");
1737         pw.println("    Example OWE: add-fake-scan fakeOwe 80:01:02:03:04:08 \"[RSN-OWE-CCMP]\" "
1738                 + "2412 -55");
1739         pw.println(
1740                 "    Example WPA2/WPA3 transition mode: add-fake-scan fakeWpa2t3 80:01:02:03:04:09 "
1741                         + "\"[WPA2-PSK-CCMP][RSN-PSK+SAE-CCMP][ESS][MFPC]\" 2412 -55");
1742         pw.println(
1743                 "    Example Open/OWE transition mode: add-fake-scan fakeOpenOwe 80:01:02:03:04:0A "
1744                         + "\"[RSN-OWE_TRANSITION-CCMP][ESS]\" 2412 -55");
1745         pw.println(
1746                 "    Example Passpoint: add-fake-scan fakePasspoint 80:01:02:03:04:0B "
1747                         + "\"[WPA2-EAP/SHA1-CCMP][RSN-EAP/SHA1-CCMP][ESS][MFPR][MFPC]"
1748                         + "[PASSPOINT]\" 2412 -55");
1749         pw.println("  reset-fake-scans");
1750         pw.println("    Resets all fake scan results added by 'add-fake-scan'.");
1751         pw.println("  enable-scanning enabled|disabled [-h]");
1752         pw.println("    Sets whether all scanning should be enabled or disabled");
1753         pw.println("    -h - Enable scanning for hidden networks.");
1754     }
1755 
1756     @Override
onHelp()1757     public void onHelp() {
1758         final PrintWriter pw = getOutPrintWriter();
1759         pw.println("Wi-Fi (wifi) commands:");
1760         pw.println("  help or -h");
1761         pw.println("    Print this help text.");
1762         onHelpNonPrivileged(pw);
1763         if (Binder.getCallingUid() == Process.ROOT_UID) {
1764             onHelpPrivileged(pw);
1765         }
1766         pw.println();
1767     }
1768 
printWifiNetworkSuggestions(PrintWriter pw, Collection<WifiNetworkSuggestion> suggestions)1769     private void printWifiNetworkSuggestions(PrintWriter pw,
1770             Collection<WifiNetworkSuggestion> suggestions) {
1771         if (suggestions == null || suggestions.isEmpty()) {
1772             pw.println("No suggestions on this device");
1773         } else {
1774             pw.println("SSID                         Security type(s)");
1775             for (WifiNetworkSuggestion suggestion : suggestions) {
1776                 pw.println(String.format("%-32s %-4s",
1777                         WifiInfo.sanitizeSsid(suggestion.getWifiConfiguration().SSID),
1778                         suggestion.getWifiConfiguration().getSecurityParamsList().stream()
1779                                 .map(p -> WifiConfiguration.getSecurityTypeName(
1780                                         p.getSecurityType())
1781                                         + (p.isAddedByAutoUpgrade() ? "^" : ""))
1782                                 .collect(Collectors.joining("/"))));
1783             }
1784         }
1785     }
1786 }
1787