| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi; |
| |
| import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; |
| import static android.net.NetworkCapabilities.TRANSPORT_WIFI; |
| import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED; |
| import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; |
| |
| import android.content.Context; |
| import android.content.pm.ParceledListSlice; |
| import android.net.ConnectivityManager; |
| import android.net.MacAddress; |
| import android.net.Network; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkRequest; |
| import android.net.wifi.IActionListener; |
| import android.net.wifi.IScoreUpdateObserver; |
| import android.net.wifi.IWifiConnectedNetworkScorer; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.SoftApConfiguration; |
| import android.net.wifi.SupplicantState; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiInfo; |
| import android.net.wifi.WifiNetworkSpecifier; |
| import android.net.wifi.WifiNetworkSuggestion; |
| import android.net.wifi.WifiScanner; |
| import android.net.wifi.nl80211.WifiNl80211Manager; |
| import android.os.BasicShellCommandHandler; |
| import android.os.Binder; |
| import android.os.Process; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.text.TextUtils; |
| import android.util.Pair; |
| |
| import com.android.server.wifi.util.ApConfigUtil; |
| import com.android.server.wifi.util.ArrayUtils; |
| import com.android.server.wifi.util.GeneralUtil; |
| import com.android.server.wifi.util.ScanResultUtil; |
| |
| import java.io.PrintWriter; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ArrayBlockingQueue; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Interprets and executes 'adb shell cmd wifi [args]'. |
| * |
| * To add new commands: |
| * - onCommand: Add a case "<command>" execute. Return a 0 |
| * if command executed successfully. |
| * - onHelp: add a description string. |
| * |
| * Permissions: currently root permission is required for some commands. Others will |
| * enforce the corresponding API permissions. |
| */ |
| public class WifiShellCommand extends BasicShellCommandHandler { |
| private static String SHELL_PACKAGE_NAME = "com.android.shell"; |
| // These don't require root access. |
| // However, these do perform permission checks in the corresponding WifiService methods. |
| private static final String[] NON_PRIVILEGED_COMMANDS = { |
| "add-suggestion", |
| "add-network", |
| "connect-network", |
| "forget-network", |
| "get-country-code", |
| "help", |
| "-h", |
| "list-scan-results", |
| "list-networks", |
| "list-suggestions", |
| "remove-suggestion", |
| "remove-all-suggestions", |
| "reset-connected-score", |
| "set-connected-score", |
| "set-scan-always-available", |
| "set-verbose-logging", |
| "set-wifi-enabled", |
| "start-scan", |
| "start-softap", |
| "status", |
| "stop-softap", |
| }; |
| |
| private static final Map<String, Pair<NetworkRequest, ConnectivityManager.NetworkCallback>> |
| sActiveRequests = new ConcurrentHashMap<>(); |
| |
| private final ClientModeImpl mClientModeImpl; |
| private final WifiLockManager mWifiLockManager; |
| private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; |
| private final WifiConfigManager mWifiConfigManager; |
| private final WifiNative mWifiNative; |
| private final HostapdHal mHostapdHal; |
| private final WifiCountryCode mWifiCountryCode; |
| private final WifiLastResortWatchdog mWifiLastResortWatchdog; |
| private final WifiServiceImpl mWifiService; |
| private final Context mContext; |
| private final ConnectivityManager mConnectivityManager; |
| private final WifiCarrierInfoManager mWifiCarrierInfoManager; |
| private final ActiveModeWarden mActiveModeWarden; |
| |
| WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, Context context) { |
| mClientModeImpl = wifiInjector.getClientModeImpl(); |
| mWifiLockManager = wifiInjector.getWifiLockManager(); |
| mWifiNetworkSuggestionsManager = wifiInjector.getWifiNetworkSuggestionsManager(); |
| mWifiConfigManager = wifiInjector.getWifiConfigManager(); |
| mHostapdHal = wifiInjector.getHostapdHal(); |
| mWifiNative = wifiInjector.getWifiNative(); |
| mWifiCountryCode = wifiInjector.getWifiCountryCode(); |
| mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog(); |
| mWifiService = wifiService; |
| mContext = context; |
| mConnectivityManager = context.getSystemService(ConnectivityManager.class); |
| mWifiCarrierInfoManager = wifiInjector.getWifiCarrierInfoManager(); |
| mActiveModeWarden = wifiInjector.getActiveModeWarden(); |
| } |
| |
| @Override |
| public int onCommand(String cmd) { |
| // Treat no command as help command. |
| if (cmd == null || cmd.equals("")) { |
| cmd = "help"; |
| } |
| // Explicit exclusion from root permission |
| if (ArrayUtils.indexOf(NON_PRIVILEGED_COMMANDS, cmd) == -1) { |
| final int uid = Binder.getCallingUid(); |
| if (uid != Process.ROOT_UID) { |
| throw new SecurityException( |
| "Uid " + uid + " does not have access to " + cmd + " wifi command"); |
| } |
| } |
| |
| final PrintWriter pw = getOutPrintWriter(); |
| try { |
| switch (cmd) { |
| case "set-ipreach-disconnect": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| mClientModeImpl.setIpReachabilityDisconnectEnabled(enabled); |
| return 0; |
| } |
| case "get-ipreach-disconnect": |
| pw.println("IPREACH_DISCONNECT state is " |
| + mClientModeImpl.getIpReachabilityDisconnectEnabled()); |
| return 0; |
| case "set-poll-rssi-interval-msecs": |
| int newPollIntervalMsecs; |
| try { |
| newPollIntervalMsecs = Integer.parseInt(getNextArgRequired()); |
| } catch (NumberFormatException e) { |
| pw.println( |
| "Invalid argument to 'set-poll-rssi-interval-msecs' " |
| + "- must be a positive integer"); |
| return -1; |
| } |
| |
| if (newPollIntervalMsecs < 1) { |
| pw.println( |
| "Invalid argument to 'set-poll-rssi-interval-msecs' " |
| + "- must be a positive integer"); |
| return -1; |
| } |
| |
| mClientModeImpl.setPollRssiIntervalMsecs(newPollIntervalMsecs); |
| return 0; |
| case "get-poll-rssi-interval-msecs": |
| pw.println("ClientModeImpl.mPollRssiIntervalMsecs = " |
| + mClientModeImpl.getPollRssiIntervalMsecs()); |
| return 0; |
| case "force-hi-perf-mode": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| if (!mWifiLockManager.forceHiPerfMode(enabled)) { |
| pw.println("Command execution failed"); |
| } |
| return 0; |
| } |
| case "force-low-latency-mode": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| if (!mWifiLockManager.forceLowLatencyMode(enabled)) { |
| pw.println("Command execution failed"); |
| } |
| return 0; |
| } |
| case "network-suggestions-set-user-approved": { |
| String packageName = getNextArgRequired(); |
| boolean approved = getNextArgRequiredTrueOrFalse("yes", "no"); |
| mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(approved, packageName); |
| return 0; |
| } |
| case "network-suggestions-has-user-approved": { |
| String packageName = getNextArgRequired(); |
| boolean hasUserApproved = |
| mWifiNetworkSuggestionsManager.hasUserApprovedForApp(packageName); |
| pw.println(hasUserApproved ? "yes" : "no"); |
| return 0; |
| } |
| case "imsi-protection-exemption-set-user-approved-for-carrier": { |
| String arg1 = getNextArgRequired(); |
| int carrierId = -1; |
| try { |
| carrierId = Integer.parseInt(arg1); |
| } catch (NumberFormatException e) { |
| pw.println("Invalid argument to " |
| + "'imsi-protection-exemption-set-user-approved-for-carrier' " |
| + "- carrierId must be an Integer"); |
| return -1; |
| } |
| boolean approved = getNextArgRequiredTrueOrFalse("yes", "no"); |
| mWifiCarrierInfoManager |
| .setHasUserApprovedImsiPrivacyExemptionForCarrier(approved, carrierId); |
| return 0; |
| } |
| case "imsi-protection-exemption-has-user-approved-for-carrier": { |
| String arg1 = getNextArgRequired(); |
| int carrierId = -1; |
| try { |
| carrierId = Integer.parseInt(arg1); |
| } catch (NumberFormatException e) { |
| pw.println("Invalid argument to " |
| + "'imsi-protection-exemption-has-user-approved-for-carrier' " |
| + "- 'carrierId' must be an Integer"); |
| return -1; |
| } |
| boolean hasUserApproved = mWifiCarrierInfoManager |
| .hasUserApprovedImsiPrivacyExemptionForCarrier(carrierId); |
| pw.println(hasUserApproved ? "yes" : "no"); |
| return 0; |
| } |
| case "imsi-protection-exemption-clear-user-approved-for-carrier": { |
| String arg1 = getNextArgRequired(); |
| int carrierId = -1; |
| try { |
| carrierId = Integer.parseInt(arg1); |
| } catch (NumberFormatException e) { |
| pw.println("Invalid argument to " |
| + "'imsi-protection-exemption-clear-user-approved-for-carrier' " |
| + "- 'carrierId' must be an Integer"); |
| return -1; |
| } |
| mWifiCarrierInfoManager.clearImsiPrivacyExemptionForCarrier(carrierId); |
| return 0; |
| } |
| case "network-requests-remove-user-approved-access-points": { |
| String packageName = getNextArgRequired(); |
| mClientModeImpl.removeNetworkRequestUserApprovedAccessPointsForApp(packageName); |
| return 0; |
| } |
| case "clear-user-disabled-networks": { |
| mWifiConfigManager.clearUserTemporarilyDisabledList(); |
| return 0; |
| } |
| case "send-link-probe": { |
| return sendLinkProbe(pw); |
| } |
| case "force-softap-channel": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| if (enabled) { |
| int apChannelMHz; |
| try { |
| apChannelMHz = Integer.parseInt(getNextArgRequired()); |
| } catch (NumberFormatException e) { |
| pw.println("Invalid argument to 'force-softap-channel enabled' " |
| + "- must be a positive integer"); |
| return -1; |
| } |
| int apChannel = ScanResult.convertFrequencyMhzToChannel(apChannelMHz); |
| int band = ApConfigUtil.convertFrequencyToBand(apChannelMHz); |
| if (apChannel == -1 || band == -1 || !isApChannelMHzValid(apChannelMHz)) { |
| pw.println("Invalid argument to 'force-softap-channel enabled' " |
| + "- must be a valid WLAN channel"); |
| return -1; |
| } |
| |
| if ((band == SoftApConfiguration.BAND_5GHZ |
| && !mWifiService.is5GHzBandSupported()) |
| || (band == SoftApConfiguration.BAND_6GHZ |
| && !mWifiService.is6GHzBandSupported())) { |
| pw.println("Invalid argument to 'force-softap-channel enabled' " |
| + "- channel band is not supported by the device"); |
| return -1; |
| } |
| |
| mHostapdHal.enableForceSoftApChannel(apChannel, band); |
| return 0; |
| } else { |
| mHostapdHal.disableForceSoftApChannel(); |
| return 0; |
| } |
| } |
| case "start-softap": { |
| SoftApConfiguration config = buildSoftApConfiguration(pw); |
| if (mWifiService.startTetheredHotspot(config)) { |
| pw.println("Soft AP started successfully"); |
| } else { |
| pw.println("Soft AP failed to start. Please check config parameters"); |
| } |
| return 0; |
| } |
| case "stop-softap": { |
| if (mWifiService.stopSoftAp()) { |
| pw.println("Soft AP stopped successfully"); |
| } else { |
| pw.println("Soft AP failed to stop"); |
| } |
| return 0; |
| } |
| case "force-country-code": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| if (enabled) { |
| String countryCode = getNextArgRequired(); |
| if (!(countryCode.length() == 2 |
| && countryCode.chars().allMatch(Character::isLetter))) { |
| pw.println("Invalid argument to 'force-country-code enabled' " |
| + "- must be a two-letter string"); |
| return -1; |
| } |
| mWifiCountryCode.enableForceCountryCode(countryCode); |
| return 0; |
| } else { |
| mWifiCountryCode.disableForceCountryCode(); |
| return 0; |
| } |
| } |
| case "get-country-code": { |
| pw.println("Wifi Country Code = " |
| + mWifiCountryCode.getCountryCode()); |
| return 0; |
| } |
| case "set-wifi-watchdog": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| mWifiLastResortWatchdog.setWifiWatchdogFeature(enabled); |
| return 0; |
| } |
| case "get-wifi-watchdog": { |
| pw.println("wifi watchdog state is " |
| + mWifiLastResortWatchdog.getWifiWatchdogFeature()); |
| return 0; |
| } |
| case "set-wifi-enabled": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled); |
| return 0; |
| } |
| case "set-scan-always-available": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| mWifiService.setScanAlwaysAvailable(enabled); |
| return 0; |
| } |
| case "get-softap-supported-features": |
| // This command is used for vts to check softap supported features. |
| if (ApConfigUtil.isAcsSupported(mContext)) { |
| pw.println("wifi_softap_acs_supported"); |
| } |
| if (ApConfigUtil.isWpa3SaeSupported(mContext)) { |
| pw.println("wifi_softap_wpa3_sae_supported"); |
| } |
| return 0; |
| case "settings-reset": |
| mWifiService.factoryReset(SHELL_PACKAGE_NAME); |
| return 0; |
| case "list-scan-results": |
| List<ScanResult> scanResults = |
| mWifiService.getScanResults(SHELL_PACKAGE_NAME, null); |
| if (scanResults.isEmpty()) { |
| pw.println("No scan results"); |
| } else { |
| ScanResultUtil.dumpScanResults(pw, scanResults, |
| SystemClock.elapsedRealtime()); |
| } |
| return 0; |
| case "start-scan": |
| mWifiService.startScan(SHELL_PACKAGE_NAME, null); |
| return 0; |
| case "list-networks": |
| ParceledListSlice<WifiConfiguration> networks = |
| mWifiService.getConfiguredNetworks(SHELL_PACKAGE_NAME, null); |
| if (networks == null || networks.getList().isEmpty()) { |
| pw.println("No networks"); |
| } else { |
| pw.println("Network Id SSID Security type"); |
| for (WifiConfiguration network : networks.getList()) { |
| String securityType = null; |
| if (WifiConfigurationUtil.isConfigForSaeNetwork(network)) { |
| securityType = "wpa3"; |
| } else if (WifiConfigurationUtil.isConfigForPskNetwork(network)) { |
| securityType = "wpa2"; |
| } else if (WifiConfigurationUtil.isConfigForEapNetwork(network)) { |
| securityType = "eap"; |
| } else if (WifiConfigurationUtil.isConfigForOweNetwork(network)) { |
| securityType = "owe"; |
| } else if (WifiConfigurationUtil.isConfigForOpenNetwork(network)) { |
| securityType = "open"; |
| } |
| pw.println(String.format("%-12d %-32s %-4s", |
| network.networkId, WifiInfo.sanitizeSsid(network.SSID), |
| securityType)); |
| } |
| } |
| return 0; |
| case "connect-network": { |
| CountDownLatch countDownLatch = new CountDownLatch(1); |
| IActionListener.Stub actionListener = new IActionListener.Stub() { |
| @Override |
| public void onSuccess() throws RemoteException { |
| pw.println("Connection initiated "); |
| countDownLatch.countDown(); |
| } |
| |
| @Override |
| public void onFailure(int i) throws RemoteException { |
| pw.println("Connection failed"); |
| countDownLatch.countDown(); |
| } |
| }; |
| WifiConfiguration config = buildWifiConfiguration(pw); |
| mWifiService.connect( |
| config, -1, new Binder(), actionListener, actionListener.hashCode()); |
| // wait for status. |
| countDownLatch.await(500, TimeUnit.MILLISECONDS); |
| setAutoJoin(pw, config.SSID, config.allowAutojoin); |
| return 0; |
| } |
| case "add-network": { |
| CountDownLatch countDownLatch = new CountDownLatch(1); |
| IActionListener.Stub actionListener = new IActionListener.Stub() { |
| @Override |
| public void onSuccess() throws RemoteException { |
| pw.println("Save successful"); |
| countDownLatch.countDown(); |
| } |
| |
| @Override |
| public void onFailure(int i) throws RemoteException { |
| pw.println("Save failed"); |
| countDownLatch.countDown(); |
| } |
| }; |
| WifiConfiguration config = buildWifiConfiguration(pw); |
| mWifiService.save( |
| config, new Binder(), actionListener, actionListener.hashCode()); |
| // wait for status. |
| countDownLatch.await(500, TimeUnit.MILLISECONDS); |
| setAutoJoin(pw, config.SSID, config.allowAutojoin); |
| return 0; |
| } |
| case "forget-network": { |
| String networkId = getNextArgRequired(); |
| CountDownLatch countDownLatch = new CountDownLatch(1); |
| IActionListener.Stub actionListener = new IActionListener.Stub() { |
| @Override |
| public void onSuccess() throws RemoteException { |
| pw.println("Forget successful"); |
| countDownLatch.countDown(); |
| } |
| |
| @Override |
| public void onFailure(int i) throws RemoteException { |
| pw.println("Forget failed"); |
| countDownLatch.countDown(); |
| } |
| }; |
| mWifiService.forget( |
| Integer.parseInt(networkId), new Binder(), actionListener, |
| actionListener.hashCode()); |
| // wait for status. |
| countDownLatch.await(500, TimeUnit.MILLISECONDS); |
| return 0; |
| } |
| case "status": |
| printStatus(pw); |
| return 0; |
| case "set-verbose-logging": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| mWifiService.enableVerboseLogging(enabled ? 1 : 0); |
| return 0; |
| } |
| case "add-suggestion": { |
| WifiNetworkSuggestion suggestion = buildSuggestion(pw); |
| mWifiService.addNetworkSuggestions( |
| Arrays.asList(suggestion), SHELL_PACKAGE_NAME, null); |
| return 0; |
| } |
| case "remove-suggestion": { |
| String ssid = getNextArgRequired(); |
| List<WifiNetworkSuggestion> suggestions = |
| mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME); |
| WifiNetworkSuggestion suggestion = suggestions.stream() |
| .filter(s -> s.getSsid().equals(ssid)) |
| .findAny() |
| .orElse(null); |
| if (suggestion == null) { |
| pw.println("No matching suggestion to remove"); |
| return -1; |
| } |
| mWifiService.removeNetworkSuggestions( |
| Arrays.asList(suggestion), SHELL_PACKAGE_NAME); |
| return 0; |
| } |
| case "remove-all-suggestions": |
| mWifiService.removeNetworkSuggestions( |
| Collections.emptyList(), SHELL_PACKAGE_NAME); |
| return 0; |
| case "list-suggestions": { |
| List<WifiNetworkSuggestion> suggestions = |
| mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME); |
| printWifiNetworkSuggestions(pw, suggestions); |
| return 0; |
| } |
| case "list-all-suggestions": { |
| Set<WifiNetworkSuggestion> suggestions = |
| mWifiNetworkSuggestionsManager.getAllNetworkSuggestions(); |
| printWifiNetworkSuggestions(pw, suggestions); |
| return 0; |
| } |
| case "list-suggestions-from-app": { |
| String packageName = getNextArgRequired(); |
| List<WifiNetworkSuggestion> suggestions = |
| mWifiService.getNetworkSuggestions(packageName); |
| printWifiNetworkSuggestions(pw, suggestions); |
| return 0; |
| } |
| case "add-request": { |
| NetworkRequest networkRequest = buildNetworkRequest(pw); |
| ConnectivityManager.NetworkCallback networkCallback = |
| new ConnectivityManager.NetworkCallback(); |
| pw.println("Adding request: " + networkRequest); |
| mConnectivityManager.requestNetwork(networkRequest, networkCallback); |
| String ssid = getAllArgs()[1]; |
| sActiveRequests.put(ssid, Pair.create(networkRequest, networkCallback)); |
| return 0; |
| } |
| case "remove-request": { |
| String ssid = getNextArgRequired(); |
| Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc = |
| sActiveRequests.remove(ssid); |
| if (nrAndNc == null) { |
| pw.println("No matching request to remove"); |
| return -1; |
| } |
| pw.println("Removing request: " + nrAndNc.first); |
| mConnectivityManager.unregisterNetworkCallback(nrAndNc.second); |
| return 0; |
| } |
| case "remove-all-requests": |
| if (sActiveRequests.isEmpty()) { |
| pw.println("No active requests"); |
| return -1; |
| } |
| for (Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc |
| : sActiveRequests.values()) { |
| pw.println("Removing request: " + nrAndNc.first); |
| mConnectivityManager.unregisterNetworkCallback(nrAndNc.second); |
| } |
| sActiveRequests.clear(); |
| return 0; |
| case "list-requests": |
| if (sActiveRequests.isEmpty()) { |
| pw.println("No active requests"); |
| } else { |
| pw.println("SSID NetworkRequest"); |
| for (Map.Entry<String, |
| Pair<NetworkRequest, ConnectivityManager.NetworkCallback>> entry : |
| sActiveRequests.entrySet()) { |
| pw.println(String.format("%-32s %-4s", |
| entry.getKey(), entry.getValue().first)); |
| } |
| } |
| return 0; |
| case "network-requests-set-user-approved": { |
| String packageName = getNextArgRequired(); |
| boolean approved = getNextArgRequiredTrueOrFalse("yes", "no"); |
| mClientModeImpl.setNetworkRequestUserApprovedApp(packageName, approved); |
| return 0; |
| } |
| case "network-requests-has-user-approved": { |
| String packageName = getNextArgRequired(); |
| boolean hasUserApproved = |
| mClientModeImpl.hasNetworkRequestUserApprovedApp(packageName); |
| pw.println(hasUserApproved ? "yes" : "no"); |
| return 0; |
| } |
| case "set-connected-score": { |
| int score = Integer.parseInt(getNextArgRequired()); |
| CountDownLatch countDownLatch = new CountDownLatch(2); |
| GeneralUtil.Mutable<IScoreUpdateObserver> scoreUpdateObserverMutable = |
| new GeneralUtil.Mutable<>(); |
| GeneralUtil.Mutable<Integer> sessionIdMutable = new GeneralUtil.Mutable<>(); |
| IWifiConnectedNetworkScorer.Stub connectedScorer = |
| new IWifiConnectedNetworkScorer.Stub() { |
| @Override |
| public void onStart(int sessionId) { |
| sessionIdMutable.value = sessionId; |
| countDownLatch.countDown(); |
| } |
| @Override |
| public void onStop(int sessionId) { |
| // clear the external scorer on disconnect. |
| mWifiService.clearWifiConnectedNetworkScorer(); |
| } |
| @Override |
| public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) { |
| scoreUpdateObserverMutable.value = observerImpl; |
| countDownLatch.countDown(); |
| } |
| }; |
| mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer |
| if (mWifiService.setWifiConnectedNetworkScorer(new Binder(), connectedScorer)) { |
| // wait for retrieving the session id & score observer. |
| countDownLatch.await(1000, TimeUnit.MILLISECONDS); |
| } |
| if (scoreUpdateObserverMutable.value == null |
| || sessionIdMutable.value == null) { |
| pw.println("Did not receive session id and/or the score update observer. " |
| + "Is the device connected to a wifi network?"); |
| mWifiService.clearWifiConnectedNetworkScorer(); |
| return -1; |
| } |
| pw.println("Updating score: " + score + " for session id: " |
| + sessionIdMutable.value); |
| try { |
| scoreUpdateObserverMutable.value.notifyScoreUpdate( |
| sessionIdMutable.value, score); |
| } catch (RemoteException e) { |
| pw.println("Failed to send the score update"); |
| mWifiService.clearWifiConnectedNetworkScorer(); |
| return -1; |
| } |
| return 0; |
| } |
| case "reset-connected-score": { |
| mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer |
| return 0; |
| } |
| case "set-emergency-callback-mode": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| mActiveModeWarden.emergencyCallbackModeChanged(enabled); |
| return 0; |
| } |
| case "set-emergency-call-state": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| mActiveModeWarden.emergencyCallStateChanged(enabled); |
| return 0; |
| } |
| default: |
| return handleDefaultCommands(cmd); |
| } |
| } catch (IllegalArgumentException e) { |
| pw.println("Invalid args for " + cmd + ": " + e); |
| return -1; |
| } catch (Exception e) { |
| pw.println("Exception while executing WifiShellCommand: "); |
| e.printStackTrace(pw); |
| return -1; |
| } |
| } |
| |
| private boolean getNextArgRequiredTrueOrFalse(String trueString, String falseString) |
| throws IllegalArgumentException { |
| String nextArg = getNextArgRequired(); |
| if (trueString.equals(nextArg)) { |
| return true; |
| } else if (falseString.equals(nextArg)) { |
| return false; |
| } else { |
| throw new IllegalArgumentException("Expected '" + trueString + "' or '" + falseString |
| + "' as next arg but got '" + nextArg + "'"); |
| } |
| } |
| |
| private WifiConfiguration buildWifiConfiguration(PrintWriter pw) { |
| String ssid = getNextArgRequired(); |
| String type = getNextArgRequired(); |
| WifiConfiguration configuration = new WifiConfiguration(); |
| configuration.SSID = "\"" + ssid + "\""; |
| if (TextUtils.equals(type, "wpa3")) { |
| configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); |
| configuration.preSharedKey = "\"" + getNextArgRequired() + "\""; |
| } else if (TextUtils.equals(type, "wpa2")) { |
| configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); |
| configuration.preSharedKey = "\"" + getNextArgRequired() + "\""; |
| } else if (TextUtils.equals(type, "owe")) { |
| configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); |
| } else if (TextUtils.equals(type, "open")) { |
| configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); |
| } else { |
| throw new IllegalArgumentException("Unknown network type " + type); |
| } |
| String option = getNextOption(); |
| while (option != null) { |
| if (option.equals("-m")) { |
| configuration.meteredOverride = METERED_OVERRIDE_METERED; |
| } else if (option.equals("-d")) { |
| configuration.allowAutojoin = false; |
| } else if (option.equals("-b")) { |
| configuration.BSSID = getNextArgRequired(); |
| } else { |
| pw.println("Ignoring unknown option " + option); |
| } |
| option = getNextOption(); |
| } |
| return configuration; |
| } |
| |
| private SoftApConfiguration buildSoftApConfiguration(PrintWriter pw) { |
| String ssid = getNextArgRequired(); |
| String type = getNextArgRequired(); |
| SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); |
| configBuilder.setSsid("\"" + ssid + "\""); |
| if (TextUtils.equals(type, "wpa2")) { |
| configBuilder.setPassphrase(getNextArgRequired(), |
| SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); |
| } else if (TextUtils.equals(type, "open")) { |
| configBuilder.setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN); |
| } else { |
| throw new IllegalArgumentException("Unknown network type " + type); |
| } |
| String option = getNextOption(); |
| while (option != null) { |
| if (option.equals("-b")) { |
| String preferredBand = getNextArgRequired(); |
| if (preferredBand.equals("2")) { |
| configBuilder.setBand(SoftApConfiguration.BAND_2GHZ); |
| } else if (preferredBand.equals("5")) { |
| configBuilder.setBand(SoftApConfiguration.BAND_5GHZ); |
| } else if (preferredBand.equals("6")) { |
| configBuilder.setBand(SoftApConfiguration.BAND_6GHZ); |
| } else if (preferredBand.equals("any")) { |
| configBuilder.setBand(SoftApConfiguration.BAND_ANY); |
| } else { |
| throw new IllegalArgumentException("Invalid band option " + preferredBand); |
| } |
| } else { |
| pw.println("Ignoring unknown option " + option); |
| } |
| option = getNextOption(); |
| } |
| return configBuilder.build(); |
| } |
| |
| private WifiNetworkSuggestion buildSuggestion(PrintWriter pw) { |
| String ssid = getNextArgRequired(); |
| String type = getNextArgRequired(); |
| WifiNetworkSuggestion.Builder suggestionBuilder = |
| new WifiNetworkSuggestion.Builder(); |
| suggestionBuilder.setSsid(ssid); |
| if (TextUtils.equals(type, "wpa3")) { |
| suggestionBuilder.setWpa3Passphrase(getNextArgRequired()); |
| } else if (TextUtils.equals(type, "wpa2")) { |
| suggestionBuilder.setWpa2Passphrase(getNextArgRequired()); |
| } else if (TextUtils.equals(type, "owe")) { |
| suggestionBuilder.setIsEnhancedOpen(true); |
| } else if (TextUtils.equals(type, "open")) { |
| // nothing to do. |
| } else { |
| throw new IllegalArgumentException("Unknown network type " + type); |
| } |
| String option = getNextOption(); |
| while (option != null) { |
| if (option.equals("-u")) { |
| suggestionBuilder.setUntrusted(true); |
| } else if (option.equals("-m")) { |
| suggestionBuilder.setIsMetered(true); |
| } else if (option.equals("-s")) { |
| suggestionBuilder.setCredentialSharedWithUser(true); |
| } else if (option.equals("-d")) { |
| suggestionBuilder.setIsInitialAutojoinEnabled(false); |
| } else if (option.equals("-b")) { |
| suggestionBuilder.setBssid(MacAddress.fromString(getNextArgRequired())); |
| } else { |
| pw.println("Ignoring unknown option " + option); |
| } |
| option = getNextOption(); |
| } |
| return suggestionBuilder.build(); |
| } |
| |
| private NetworkRequest buildNetworkRequest(PrintWriter pw) { |
| String ssid = getNextArgRequired(); |
| String type = getNextArgRequired(); |
| WifiNetworkSpecifier.Builder specifierBuilder = |
| new WifiNetworkSpecifier.Builder(); |
| specifierBuilder.setSsid(ssid); |
| if (TextUtils.equals(type, "wpa3")) { |
| specifierBuilder.setWpa3Passphrase(getNextArgRequired()); |
| } else if (TextUtils.equals(type, "wpa2")) { |
| specifierBuilder.setWpa2Passphrase(getNextArgRequired()); |
| } else if (TextUtils.equals(type, "owe")) { |
| specifierBuilder.setIsEnhancedOpen(true); |
| } else if (TextUtils.equals(type, "open")) { |
| // nothing to do. |
| } else { |
| throw new IllegalArgumentException("Unknown network type " + type); |
| } |
| String bssid = null; |
| String option = getNextOption(); |
| while (option != null) { |
| if (option.equals("-b")) { |
| bssid = getNextArgRequired(); |
| } else { |
| pw.println("Ignoring unknown option " + option); |
| } |
| option = getNextOption(); |
| } |
| |
| // Permission approval bypass is only available to requests with both ssid & bssid set. |
| // So, find scan result with the best rssi level to set in the request. |
| if (bssid == null) { |
| ScanResult matchingScanResult = |
| mWifiService.getScanResults(SHELL_PACKAGE_NAME, null) |
| .stream() |
| .filter(s -> s.SSID.equals(ssid)) |
| .max(Comparator.comparingInt(s -> s.level)) |
| .orElse(null); |
| if (matchingScanResult != null) { |
| bssid = matchingScanResult.BSSID; |
| } else { |
| pw.println("No matching bssid found, request will need UI approval"); |
| } |
| } |
| if (bssid != null) specifierBuilder.setBssid(MacAddress.fromString(bssid)); |
| return new NetworkRequest.Builder() |
| .addTransportType(TRANSPORT_WIFI) |
| .removeCapability(NET_CAPABILITY_INTERNET) |
| .setNetworkSpecifier(specifierBuilder.build()) |
| .build(); |
| } |
| |
| private void setAutoJoin(PrintWriter pw, String ssid, boolean allowAutojoin) { |
| // For suggestions, this will work only if the config has already been added |
| // to WifiConfigManager. |
| WifiConfiguration retrievedConfig = |
| mWifiService.getPrivilegedConfiguredNetworks(SHELL_PACKAGE_NAME, null) |
| .getList() |
| .stream() |
| .filter(n -> n.SSID.equals(ssid)) |
| .findAny() |
| .orElse(null); |
| if (retrievedConfig == null) { |
| pw.println("Cannot retrieve config, autojoin setting skipped."); |
| return; |
| } |
| mWifiService.allowAutojoin(retrievedConfig.networkId, allowAutojoin); |
| } |
| |
| private int sendLinkProbe(PrintWriter pw) throws InterruptedException { |
| // Note: should match WifiNl80211Manager#SEND_MGMT_FRAME_TIMEOUT_MS |
| final int sendMgmtFrameTimeoutMs = 1000; |
| |
| ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1); |
| mClientModeImpl.probeLink(new WifiNl80211Manager.SendMgmtFrameCallback() { |
| @Override |
| public void onAck(int elapsedTimeMs) { |
| queue.offer("Link probe succeeded after " + elapsedTimeMs + " ms"); |
| } |
| |
| @Override |
| public void onFailure(int reason) { |
| queue.offer("Link probe failed with reason " + reason); |
| } |
| }, -1); |
| |
| // block until msg is received, or timed out |
| String msg = queue.poll(sendMgmtFrameTimeoutMs + 1000, TimeUnit.MILLISECONDS); |
| if (msg == null) { |
| pw.println("Link probe timed out"); |
| } else { |
| pw.println(msg); |
| } |
| return 0; |
| } |
| |
| private boolean isApChannelMHzValid(int apChannelMHz) { |
| int[] allowed2gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ); |
| int[] allowed5gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ); |
| int[] allowed5gDfsFreq = |
| mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY); |
| int[] allowed6gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ); |
| if (allowed2gFreq == null) { |
| allowed2gFreq = new int[0]; |
| } |
| if (allowed5gFreq == null) { |
| allowed5gFreq = new int[0]; |
| } |
| if (allowed5gDfsFreq == null) { |
| allowed5gDfsFreq = new int[0]; |
| } |
| if (allowed6gFreq == null) { |
| allowed6gFreq = new int[0]; |
| } |
| |
| return (Arrays.binarySearch(allowed2gFreq, apChannelMHz) >= 0 |
| || Arrays.binarySearch(allowed5gFreq, apChannelMHz) >= 0 |
| || Arrays.binarySearch(allowed5gDfsFreq, apChannelMHz) >= 0) |
| || Arrays.binarySearch(allowed6gFreq, apChannelMHz) >= 0; |
| } |
| |
| private void printStatus(PrintWriter pw) { |
| boolean wifiEnabled = mWifiService.getWifiEnabledState() == WIFI_STATE_ENABLED; |
| pw.println("Wifi is " + (wifiEnabled ? "enabled" : "disabled")); |
| pw.println("Wifi scanning is " |
| + (mWifiService.isScanAlwaysAvailable() |
| ? "always available" : "only available when wifi is enabled")); |
| if (!wifiEnabled) { |
| return; |
| } |
| WifiInfo info = mWifiService.getConnectionInfo(SHELL_PACKAGE_NAME, null); |
| if (info.getSupplicantState() != SupplicantState.COMPLETED) { |
| pw.println("Wifi is not connected"); |
| return; |
| } |
| pw.println("Wifi is connected to " + info.getSSID()); |
| pw.println("WifiInfo: " + info); |
| // additional diagnostics not printed by WifiInfo.toString() |
| pw.println("successfulTxPackets: " + info.txSuccess); |
| pw.println("successfulTxPacketsPerSecond: " + info.getSuccessfulTxPacketsPerSecond()); |
| pw.println("retriedTxPackets: " + info.txRetries); |
| pw.println("retriedTxPacketsPerSecond: " + info.getRetriedTxPacketsPerSecond()); |
| pw.println("lostTxPackets: " + info.txBad); |
| pw.println("lostTxPacketsPerSecond: " + info.getLostTxPacketsPerSecond()); |
| pw.println("successfulRxPackets: " + info.rxSuccess); |
| pw.println("successfulRxPacketsPerSecond: " + info.getSuccessfulRxPacketsPerSecond()); |
| |
| Network network = mWifiService.getCurrentNetwork(); |
| try { |
| NetworkCapabilities capabilities = mConnectivityManager.getNetworkCapabilities(network); |
| pw.println("NetworkCapabilities: " + capabilities); |
| } catch (SecurityException e) { |
| // ignore on unrooted shell. |
| } |
| } |
| |
| private void onHelpNonPrivileged(PrintWriter pw) { |
| pw.println(" get-country-code"); |
| pw.println(" Gets country code as a two-letter string"); |
| pw.println(" set-wifi-enabled enabled|disabled"); |
| pw.println(" Enables/disables Wifi on this device."); |
| pw.println(" set-scan-always-available enabled|disabled"); |
| pw.println(" Sets whether scanning should be available even when wifi is off."); |
| pw.println(" list-scan-results"); |
| pw.println(" Lists the latest scan results"); |
| pw.println(" start-scan"); |
| pw.println(" Start a new scan"); |
| pw.println(" list-networks"); |
| pw.println(" Lists the saved networks"); |
| pw.println(" connect-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-m] [-d] " |
| + "[-b <bssid>]"); |
| pw.println(" Connect to a network with provided params and add to saved networks list"); |
| pw.println(" <ssid> - SSID of the network"); |
| pw.println(" open|owe|wpa2|wpa3 - Security type of the network."); |
| pw.println(" - Use 'open' or 'owe' for networks with no passphrase"); |
| pw.println(" - 'open' - Open networks (Most prevalent)"); |
| pw.println(" - 'owe' - Enhanced open networks"); |
| pw.println(" - Use 'wpa2' or 'wpa3' for networks with passphrase"); |
| pw.println(" - 'wpa2' - WPA-2 PSK networks (Most prevalent)"); |
| pw.println(" - 'wpa3' - WPA-3 PSK networks"); |
| pw.println(" -m - Mark the network metered."); |
| pw.println(" -d - Mark the network autojoin disabled."); |
| pw.println(" -b <bssid> - Set specific BSSID."); |
| pw.println(" add-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-m] [-d] " |
| + "[-b <bssid>]"); |
| pw.println(" Add/update saved network with provided params"); |
| pw.println(" <ssid> - SSID of the network"); |
| pw.println(" open|owe|wpa2|wpa3 - Security type of the network."); |
| pw.println(" - Use 'open' or 'owe' for networks with no passphrase"); |
| pw.println(" - 'open' - Open networks (Most prevalent)"); |
| pw.println(" - 'owe' - Enhanced open networks"); |
| pw.println(" - Use 'wpa2' or 'wpa3' for networks with passphrase"); |
| pw.println(" - 'wpa2' - WPA-2 PSK networks (Most prevalent)"); |
| pw.println(" - 'wpa3' - WPA-3 PSK networks"); |
| pw.println(" -m - Mark the network metered."); |
| pw.println(" -d - Mark the network autojoin disabled."); |
| pw.println(" -b <bssid> - Set specific BSSID."); |
| pw.println(" forget-network <networkId>"); |
| pw.println(" Remove the network mentioned by <networkId>"); |
| pw.println(" - Use list-networks to retrieve <networkId> for the network"); |
| pw.println(" status"); |
| pw.println(" Current wifi status"); |
| pw.println(" set-verbose-logging enabled|disabled "); |
| pw.println(" Set the verbose logging enabled or disabled"); |
| pw.println(" add-suggestion <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-u] [-m] [-s] [-d]" |
| + "[-b <bssid>]"); |
| pw.println(" Add a network suggestion with provided params"); |
| pw.println(" Use 'network-suggestions-set-user-approved " + SHELL_PACKAGE_NAME + " yes'" |
| + " to approve suggestions added via shell (Needs root access)"); |
| pw.println(" <ssid> - SSID of the network"); |
| pw.println(" open|owe|wpa2|wpa3 - Security type of the network."); |
| pw.println(" - Use 'open' or 'owe' for networks with no passphrase"); |
| pw.println(" - 'open' - Open networks (Most prevalent)"); |
| pw.println(" - 'owe' - Enhanced open networks"); |
| pw.println(" - Use 'wpa2' or 'wpa3' for networks with passphrase"); |
| pw.println(" - 'wpa2' - WPA-2 PSK networks (Most prevalent)"); |
| pw.println(" - 'wpa3' - WPA-3 PSK networks"); |
| pw.println(" -u - Mark the suggestion untrusted."); |
| pw.println(" -m - Mark the suggestion metered."); |
| pw.println(" -s - Share the suggestion with user."); |
| pw.println(" -d - Mark the suggestion autojoin disabled."); |
| pw.println(" -b <bssid> - Set specific BSSID."); |
| pw.println(" remove-suggestion <ssid>"); |
| pw.println(" Remove a network suggestion with provided SSID of the network"); |
| pw.println(" remove-all-suggestions"); |
| pw.println(" Removes all suggestions added via shell"); |
| pw.println(" list-suggestions"); |
| pw.println(" Lists the suggested networks added via shell"); |
| pw.println(" set-connected-score <score>"); |
| pw.println(" Set connected wifi network score (to choose between LTE & Wifi for " |
| + "default route)."); |
| pw.println(" This turns off the active connected scorer (default or external)."); |
| pw.println(" Only works while connected to a wifi network. This score will stay in " |
| + "effect until you call reset-connected-score or the device disconnects from the " |
| + "current network."); |
| pw.println(" <score> - Integer score should be in the range of 0 - 60"); |
| pw.println(" reset-connected-score"); |
| pw.println(" Turns on the default connected scorer."); |
| pw.println(" Note: Will clear any external scorer set."); |
| pw.println(" start-softap <ssid> (open|wpa2) <passphrase> [-b 2|5|6|any]"); |
| pw.println(" Start softap with provided params"); |
| pw.println(" Note that the shell command doesn't activate internet tethering. In some " |
| + "devices, internet sharing is possible when Wi-Fi STA is also enabled and is" |
| + "associated to another AP with internet access."); |
| pw.println(" <ssid> - SSID of the network"); |
| pw.println(" open|wpa2 - Security type of the network."); |
| pw.println(" - Use 'open' for networks with no passphrase"); |
| pw.println(" - Use 'wpa2' for networks with passphrase"); |
| pw.println(" -b 2|5|6|any - select the preferred band."); |
| pw.println(" - Use '2' to select 2.4GHz band as the preferred band"); |
| pw.println(" - Use '5' to select 5GHz band as the preferred band"); |
| pw.println(" - Use '6' to select 6GHz band as the preferred band"); |
| pw.println(" - Use 'any' to indicate no band preference"); |
| pw.println(" Note: If the band option is not provided, 2.4GHz is the preferred band."); |
| pw.println(" The exact channel is auto-selected by FW unless overridden by " |
| + "force-softap-channel command"); |
| pw.println(" stop-softap"); |
| pw.println(" Stop softap (hotspot)"); |
| } |
| |
| private void onHelpPrivileged(PrintWriter pw) { |
| pw.println(" set-ipreach-disconnect enabled|disabled"); |
| pw.println(" Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects."); |
| pw.println(" get-ipreach-disconnect"); |
| pw.println(" Gets setting of CMD_IP_REACHABILITY_LOST events triggering disconnects."); |
| pw.println(" set-poll-rssi-interval-msecs <int>"); |
| pw.println(" Sets the interval between RSSI polls to <int> milliseconds."); |
| pw.println(" get-poll-rssi-interval-msecs"); |
| pw.println(" Gets current interval between RSSI polls, in milliseconds."); |
| pw.println(" force-hi-perf-mode enabled|disabled"); |
| pw.println(" Sets whether hi-perf mode is forced or left for normal operation."); |
| pw.println(" force-low-latency-mode enabled|disabled"); |
| pw.println(" Sets whether low latency mode is forced or left for normal operation."); |
| pw.println(" network-suggestions-set-user-approved <package name> yes|no"); |
| pw.println(" Sets whether network suggestions from the app is approved or not."); |
| pw.println(" network-suggestions-has-user-approved <package name>"); |
| pw.println(" Queries whether network suggestions from the app is approved or not."); |
| pw.println(" imsi-protection-exemption-set-user-approved-for-carrier <carrier id> yes|no"); |
| pw.println(" Sets whether Imsi protection exemption for carrier is approved or not"); |
| pw.println(" imsi-protection-exemption-has-user-approved-for-carrier <carrier id>"); |
| pw.println(" Queries whether Imsi protection exemption for carrier is approved or not"); |
| pw.println(" imsi-protection-exemption-clear-user-approved-for-carrier <carrier id>"); |
| pw.println(" Clear the user choice on Imsi protection exemption for carrier"); |
| pw.println(" network-requests-remove-user-approved-access-points <package name>"); |
| pw.println(" Removes all user approved network requests for the app."); |
| pw.println(" clear-user-disabled-networks"); |
| pw.println(" Clears the user disabled networks list."); |
| pw.println(" send-link-probe"); |
| pw.println(" Manually triggers a link probe."); |
| pw.println(" force-softap-channel enabled <int> | disabled"); |
| pw.println(" Sets whether soft AP channel is forced to <int> MHz"); |
| pw.println(" or left for normal operation."); |
| pw.println(" force-country-code enabled <two-letter code> | disabled "); |
| pw.println(" Sets country code to <two-letter code> or left for normal value"); |
| pw.println(" set-wifi-watchdog enabled|disabled"); |
| pw.println(" Sets whether wifi watchdog should trigger recovery"); |
| pw.println(" get-wifi-watchdog"); |
| pw.println(" Gets setting of wifi watchdog trigger recovery."); |
| pw.println(" get-softap-supported-features"); |
| pw.println(" Gets softap supported features. Will print 'wifi_softap_acs_supported'"); |
| pw.println(" and/or 'wifi_softap_wpa3_sae_supported', each on a separate line."); |
| pw.println(" settings-reset"); |
| pw.println(" Initiates wifi settings reset"); |
| pw.println(" add-request <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-b <bssid>]"); |
| pw.println(" Add a network request with provided params"); |
| pw.println(" Use 'network-requests-set-user-approved android yes'" |
| + " to pre-approve requests added via rooted shell (Not persisted)"); |
| pw.println(" <ssid> - SSID of the network"); |
| pw.println(" open|owe|wpa2|wpa3 - Security type of the network."); |
| pw.println(" - Use 'open' or 'owe' for networks with no passphrase"); |
| pw.println(" - 'open' - Open networks (Most prevalent)"); |
| pw.println(" - 'owe' - Enhanced open networks"); |
| pw.println(" - Use 'wpa2' or 'wpa3' for networks with passphrase"); |
| pw.println(" - 'wpa2' - WPA-2 PSK networks (Most prevalent)"); |
| pw.println(" - 'wpa3' - WPA-3 PSK networks"); |
| pw.println(" -b <bssid> - Set specific BSSID."); |
| pw.println(" remove-request <ssid>"); |
| pw.println(" Remove a network request with provided SSID of the network"); |
| pw.println(" remove-all-requests"); |
| pw.println(" Removes all active requests added via shell"); |
| pw.println(" list-requests"); |
| pw.println(" Lists the requested networks added via shell"); |
| pw.println(" network-requests-set-user-approved <package name> yes|no"); |
| pw.println(" Sets whether network requests from the app is approved or not."); |
| pw.println(" Note: Only 1 such app can be approved from the shell at a time"); |
| pw.println(" network-requests-has-user-approved <package name>"); |
| pw.println(" Queries whether network requests from the app is approved or not."); |
| pw.println(" Note: This only returns whether the app was set via the " |
| + "'network-requests-set-user-approved' shell command"); |
| pw.println(" list-all-suggestions"); |
| pw.println(" Lists all suggested networks on this device"); |
| pw.println(" list-suggestions-from-app <package name>"); |
| pw.println(" Lists the suggested networks from the app"); |
| pw.println(" set-emergency-callback-mode enabled|disabled"); |
| pw.println(" Sets whether Emergency Callback Mode (ECBM) is enabled."); |
| pw.println(" Equivalent to receiving the " |
| + "TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED broadcast."); |
| pw.println(" set-emergency-call-state enabled|disabled"); |
| pw.println(" Sets whether we are in the middle of an emergency call."); |
| pw.println("Equivalent to receiving the " |
| + "TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED broadcast."); |
| } |
| |
| @Override |
| public void onHelp() { |
| final PrintWriter pw = getOutPrintWriter(); |
| pw.println("Wi-Fi (wifi) commands:"); |
| pw.println(" help or -h"); |
| pw.println(" Print this help text."); |
| onHelpNonPrivileged(pw); |
| if (Binder.getCallingUid() == Process.ROOT_UID) { |
| onHelpPrivileged(pw); |
| } |
| pw.println(); |
| } |
| private void printWifiNetworkSuggestions(PrintWriter pw, |
| Collection<WifiNetworkSuggestion> suggestions) { |
| if (suggestions == null || suggestions.isEmpty()) { |
| pw.println("No suggestions on this device"); |
| } else { |
| pw.println("SSID Security type"); |
| for (WifiNetworkSuggestion suggestion : suggestions) { |
| String securityType = null; |
| if (suggestion.getPasspointConfig() != null) { |
| securityType = "passpoint"; |
| } else if (WifiConfigurationUtil.isConfigForSaeNetwork( |
| suggestion.getWifiConfiguration())) { |
| securityType = "wpa3"; |
| } else if (WifiConfigurationUtil.isConfigForPskNetwork( |
| suggestion.getWifiConfiguration())) { |
| securityType = "wpa2"; |
| } else if (WifiConfigurationUtil.isConfigForEapNetwork( |
| suggestion.getWifiConfiguration())) { |
| securityType = "eap"; |
| } else if (WifiConfigurationUtil.isConfigForOweNetwork( |
| suggestion.getWifiConfiguration())) { |
| securityType = "owe"; |
| } else if (WifiConfigurationUtil.isConfigForOpenNetwork( |
| suggestion.getWifiConfiguration())) { |
| securityType = "open"; |
| } |
| pw.println(String.format("%-32s %-4s", |
| WifiInfo.sanitizeSsid(suggestion.getWifiConfiguration().SSID), |
| securityType)); |
| } |
| } |
| } |
| } |