| /* |
| * 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.NET_CAPABILITY_OEM_PAID; |
| import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; |
| import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; |
| import static android.net.NetworkCapabilities.TRANSPORT_WIFI; |
| import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED; |
| import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; |
| import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; |
| |
| import static com.android.server.wifi.SelfRecovery.REASON_API_CALL; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| 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.ISoftApCallback; |
| import android.net.wifi.IWifiConnectedNetworkScorer; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.SoftApCapability; |
| import android.net.wifi.SoftApConfiguration; |
| import android.net.wifi.SoftApInfo; |
| import android.net.wifi.SupplicantState; |
| import android.net.wifi.WifiClient; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiConnectedSessionInfo; |
| import android.net.wifi.WifiInfo; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.WifiNetworkSpecifier; |
| import android.net.wifi.WifiNetworkSuggestion; |
| import android.net.wifi.WifiScanner; |
| import android.net.wifi.WifiSsid; |
| import android.os.Binder; |
| import android.os.Process; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.telephony.Annotation; |
| import android.telephony.PhysicalChannelConfig; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import android.util.Pair; |
| |
| import androidx.annotation.NonNull; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.modules.utils.BasicShellCommandHandler; |
| import com.android.modules.utils.ParceledListSlice; |
| import com.android.modules.utils.build.SdkLevel; |
| import com.android.server.wifi.ClientMode.LinkProbeCallback; |
| import com.android.server.wifi.coex.CoexManager; |
| import com.android.server.wifi.coex.CoexUtils; |
| import com.android.server.wifi.hotspot2.NetworkDetail; |
| import com.android.server.wifi.util.ApConfigUtil; |
| import com.android.server.wifi.util.ArrayUtils; |
| import com.android.server.wifi.util.ScanResultUtil; |
| |
| import java.io.PrintWriter; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| 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; |
| import java.util.stream.Collectors; |
| |
| /** |
| * 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 { |
| @VisibleForTesting |
| public 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", |
| "is-verbose-logging", |
| "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 ActiveModeWarden mActiveModeWarden; |
| private final WifiGlobals mWifiGlobals; |
| private final WifiLockManager mWifiLockManager; |
| private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; |
| private final WifiConfigManager mWifiConfigManager; |
| private final WifiNative mWifiNative; |
| private final CoexManager mCoexManager; |
| 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 WifiNetworkFactory mWifiNetworkFactory; |
| private final SelfRecovery mSelfRecovery; |
| private final WifiThreadRunner mWifiThreadRunner; |
| private final WifiApConfigStore mWifiApConfigStore; |
| private int mSapState = WifiManager.WIFI_STATE_UNKNOWN; |
| private final ScanRequestProxy mScanRequestProxy; |
| |
| /** |
| * Used for shell command testing of scorer. |
| */ |
| public static class WifiScorer extends IWifiConnectedNetworkScorer.Stub { |
| private final WifiServiceImpl mWifiService; |
| private final CountDownLatch mCountDownLatch; |
| private Integer mSessionId; |
| private IScoreUpdateObserver mScoreUpdateObserver; |
| |
| public WifiScorer(WifiServiceImpl wifiService, CountDownLatch countDownLatch) { |
| mWifiService = wifiService; |
| mCountDownLatch = countDownLatch; |
| } |
| |
| @Override |
| public void onStart(WifiConnectedSessionInfo sessionInfo) { |
| mSessionId = sessionInfo.getSessionId(); |
| mCountDownLatch.countDown(); |
| } |
| @Override |
| public void onStop(int sessionId) { |
| // clear the external scorer on disconnect. |
| mWifiService.clearWifiConnectedNetworkScorer(); |
| } |
| @Override |
| public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) { |
| mScoreUpdateObserver = observerImpl; |
| mCountDownLatch.countDown(); |
| } |
| |
| public Integer getSessionId() { |
| return mSessionId; |
| } |
| |
| public IScoreUpdateObserver getScoreUpdateObserver() { |
| return mScoreUpdateObserver; |
| } |
| } |
| |
| WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, Context context, |
| WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner) { |
| mWifiGlobals = wifiGlobals; |
| mWifiThreadRunner = wifiThreadRunner; |
| mActiveModeWarden = wifiInjector.getActiveModeWarden(); |
| mWifiLockManager = wifiInjector.getWifiLockManager(); |
| mWifiNetworkSuggestionsManager = wifiInjector.getWifiNetworkSuggestionsManager(); |
| mWifiConfigManager = wifiInjector.getWifiConfigManager(); |
| mWifiNative = wifiInjector.getWifiNative(); |
| mCoexManager = wifiInjector.getCoexManager(); |
| mWifiCountryCode = wifiInjector.getWifiCountryCode(); |
| mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog(); |
| mWifiService = wifiService; |
| mContext = context; |
| mConnectivityManager = context.getSystemService(ConnectivityManager.class); |
| mWifiCarrierInfoManager = wifiInjector.getWifiCarrierInfoManager(); |
| mWifiNetworkFactory = wifiInjector.getWifiNetworkFactory(); |
| mSelfRecovery = wifiInjector.getSelfRecovery(); |
| mWifiApConfigStore = wifiInjector.getWifiApConfigStore(); |
| mScanRequestProxy = wifiInjector.getScanRequestProxy(); |
| } |
| |
| @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 " |
| + "(or such command doesn't exist)"); |
| } |
| } |
| |
| final PrintWriter pw = getOutPrintWriter(); |
| try { |
| switch (cmd) { |
| case "set-ipreach-disconnect": { |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| mWifiGlobals.setIpReachabilityDisconnectEnabled(enabled); |
| return 0; |
| } |
| case "get-ipreach-disconnect": |
| pw.println("IPREACH_DISCONNECT state is " |
| + mWifiGlobals.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; |
| } |
| |
| mWifiGlobals.setPollRssiIntervalMillis(newPollIntervalMsecs); |
| return 0; |
| case "get-poll-rssi-interval-msecs": |
| pw.println("WifiGlobals.getPollRssiIntervalMillis() = " |
| + mWifiGlobals.getPollRssiIntervalMillis()); |
| 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, |
| Binder.getCallingUid(), 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(); |
| mWifiNetworkFactory.removeUserApprovedAccessPointsForApp(packageName); |
| return 0; |
| } |
| case "clear-user-disabled-networks": { |
| mWifiConfigManager.clearUserTemporarilyDisabledList(); |
| return 0; |
| } |
| case "send-link-probe": { |
| return sendLinkProbe(pw); |
| } |
| case "force-softap-band": { |
| boolean forceBandEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| if (forceBandEnabled) { |
| String forcedBand = getNextArgRequired(); |
| if (forcedBand.equals("2")) { |
| mWifiApConfigStore.enableForceSoftApBandOrChannel( |
| SoftApConfiguration.BAND_2GHZ, 0); |
| } else if (forcedBand.equals("5")) { |
| mWifiApConfigStore.enableForceSoftApBandOrChannel( |
| SoftApConfiguration.BAND_5GHZ, 0); |
| } else if (forcedBand.equals("6")) { |
| mWifiApConfigStore.enableForceSoftApBandOrChannel( |
| SoftApConfiguration.BAND_6GHZ, 0); |
| } else { |
| pw.println("Invalid argument to 'force-softap-band enabled' " |
| + "- must be a valid band integer (2|5|6)"); |
| return -1; |
| } |
| return 0; |
| } else { |
| mWifiApConfigStore.disableForceSoftApBandOrChannel(); |
| return 0; |
| } |
| |
| } |
| 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.convertFrequencyMhzToChannelIfSupported( |
| apChannelMHz); |
| int band = ApConfigUtil.convertFrequencyToBand(apChannelMHz); |
| pw.println("channel: " + apChannel + " band: " + band); |
| if (apChannel == -1 || band == -1) { |
| pw.println("Invalid argument to 'force-softap-channel enabled' " |
| + "- must be a valid WLAN channel"); |
| return -1; |
| } |
| boolean isTemporarilyEnablingWifiNeeded = mWifiService.getWifiEnabledState() |
| != WIFI_STATE_ENABLED; |
| if (isTemporarilyEnablingWifiNeeded) { |
| waitForWifiEnabled(true); |
| } |
| // Following calls will fail if wifi is not enabled |
| boolean isValidChannel = isApChannelMHzValid(pw, apChannelMHz); |
| if (isTemporarilyEnablingWifiNeeded) { |
| waitForWifiEnabled(false); |
| } |
| if (!isValidChannel |
| || (band == SoftApConfiguration.BAND_5GHZ |
| && !mWifiService.is5GHzBandSupported()) |
| || (band == SoftApConfiguration.BAND_6GHZ |
| && !mWifiService.is6GHzBandSupported()) |
| || (band == SoftApConfiguration.BAND_60GHZ |
| && !mWifiService.is60GHzBandSupported())) { |
| pw.println("Invalid argument to 'force-softap-channel enabled' " |
| + "- must be a valid WLAN channel" |
| + " in a band supported by the device"); |
| return -1; |
| } |
| mWifiApConfigStore.enableForceSoftApBandOrChannel(band, apChannel); |
| return 0; |
| } else { |
| mWifiApConfigStore.disableForceSoftApBandOrChannel(); |
| return 0; |
| } |
| } |
| case "start-softap": { |
| CountDownLatch countDownLatch = new CountDownLatch(1); |
| ISoftApCallback.Stub softApCallback = new ISoftApCallback.Stub() { |
| @Override |
| public void onStateChanged(int state, int failureReason) { |
| pw.println("onStateChanged with state: " + state |
| + " failure reason: " + failureReason); |
| mSapState = state; |
| if (state == WifiManager.WIFI_AP_STATE_ENABLED) { |
| pw.println(" SAP is enabled successfully"); |
| // Skip countDown() and wait for onInfoChanged() which has |
| // the confirmed softAp channel information |
| } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) { |
| pw.println(" SAP is disabled"); |
| } else if (state == WifiManager.WIFI_AP_STATE_FAILED) { |
| pw.println(" SAP failed to start"); |
| countDownLatch.countDown(); |
| } |
| } |
| |
| @Override |
| public void onConnectedClientsOrInfoChanged(Map<String, SoftApInfo> infos, |
| Map<String, List<WifiClient>> clients, boolean isBridged, |
| boolean isRegistration) { |
| if (mSapState == WifiManager.WIFI_AP_STATE_ENABLED) { |
| countDownLatch.countDown(); |
| } |
| } |
| |
| @Override |
| public void onCapabilityChanged(SoftApCapability capability) { |
| pw.println("onCapabilityChanged " + capability); |
| } |
| |
| @Override |
| public void onBlockedClientConnecting(WifiClient client, int reason) { |
| } |
| |
| }; |
| mWifiService.registerSoftApCallback(softApCallback); |
| SoftApConfiguration config = buildSoftApConfiguration(pw); |
| if (!mWifiService.startTetheredHotspot(config, SHELL_PACKAGE_NAME)) { |
| pw.println("Soft AP failed to start. Please check config parameters"); |
| } |
| // Wait for softap to start and complete callback |
| countDownLatch.await(3000, TimeUnit.MILLISECONDS); |
| mWifiService.unregisterSoftApCallback(softApCallback); |
| 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 (!WifiCountryCode.isValid(countryCode)) { |
| pw.println("Invalid argument: Country code must be a 2-Character" |
| + " alphanumeric code. But got countryCode " + countryCode |
| + " instead"); |
| return -1; |
| } |
| mWifiCountryCode.setOverrideCountryCode(countryCode); |
| return 0; |
| } else { |
| mWifiCountryCode.clearOverrideCountryCode(); |
| 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, SHELL_PACKAGE_NAME); |
| 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"); |
| } |
| if ((mWifiService.getSupportedFeatures() |
| & WifiManager.WIFI_FEATURE_BRIDGED_AP) |
| == WifiManager.WIFI_FEATURE_BRIDGED_AP) { |
| pw.println("wifi_softap_bridged_ap_supported"); |
| } |
| if ((mWifiService.getSupportedFeatures() |
| & WifiManager.WIFI_FEATURE_STA_BRIDGED_AP) |
| == WifiManager.WIFI_FEATURE_STA_BRIDGED_AP) { |
| pw.println("wifi_softap_bridged_ap_with_sta_supported"); |
| } |
| return 0; |
| case "settings-reset": |
| mWifiNative.stopFakingScanDetails(); |
| mWifiNative.resetFakeScanDetails(); |
| 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, false); |
| 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 = network.getSecurityParamsList().stream() |
| .map(p -> WifiConfiguration.getSecurityTypeName( |
| p.getSecurityType()) |
| + (p.isAddedByAutoUpgrade() ? "^" : "")) |
| .collect(Collectors.joining("/")); |
| 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, actionListener); |
| // 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, actionListener); |
| // 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), actionListener); |
| // wait for status. |
| countDownLatch.await(500, TimeUnit.MILLISECONDS); |
| return 0; |
| } |
| case "pmksa-flush": { |
| String networkId = getNextArgRequired(); |
| int netId = Integer.parseInt(networkId); |
| WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(netId); |
| if (config == null) { |
| pw.println("No Wifi config corresponding to networkId: " + netId); |
| return -1; |
| } |
| mWifiNative.removeNetworkCachedData(netId); |
| 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 "is-verbose-logging": { |
| int enabled = mWifiService.getVerboseLoggingLevel(); |
| pw.println(enabled > 0 ? "enabled" : "disabled"); |
| return 0; |
| } |
| case "start-restricting-auto-join-to-subscription-id": { |
| if (!SdkLevel.isAtLeastS()) { |
| pw.println("This feature is only supported on SdkLevel S or later."); |
| return -1; |
| } |
| int subId = Integer.parseInt(getNextArgRequired()); |
| mWifiService.startRestrictingAutoJoinToSubscriptionId(subId); |
| return 0; |
| } |
| case "stop-restricting-auto-join-to-subscription-id": { |
| if (!SdkLevel.isAtLeastS()) { |
| pw.println("This feature is only supported on SdkLevel S or later."); |
| return -1; |
| } |
| mWifiService.stopRestrictingAutoJoinToSubscriptionId(); |
| return 0; |
| } |
| case "add-suggestion": { |
| WifiNetworkSuggestion suggestion = buildSuggestion(pw); |
| if (suggestion == null) { |
| pw.println("Invalid network suggestion parameter"); |
| return -1; |
| } |
| int errorCode = mWifiService.addNetworkSuggestions( |
| Arrays.asList(suggestion), SHELL_PACKAGE_NAME, null); |
| if (errorCode != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { |
| pw.println("Add network suggestion failed with error code: " + errorCode); |
| return -1; |
| } |
| // untrusted/oem-paid networks need a corresponding NetworkRequest. |
| if (suggestion.isUntrusted() |
| || (SdkLevel.isAtLeastS() |
| && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) { |
| NetworkRequest.Builder networkRequestBuilder = |
| new NetworkRequest.Builder() |
| .addTransportType(TRANSPORT_WIFI); |
| if (suggestion.isUntrusted()) { |
| networkRequestBuilder.removeCapability(NET_CAPABILITY_TRUSTED); |
| } |
| if (SdkLevel.isAtLeastS()) { |
| if (suggestion.isOemPaid()) { |
| networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PAID); |
| } |
| if (suggestion.isOemPrivate()) { |
| networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PRIVATE); |
| } |
| } |
| NetworkRequest networkRequest = networkRequestBuilder.build(); |
| ConnectivityManager.NetworkCallback networkCallback = |
| new ConnectivityManager.NetworkCallback(); |
| pw.println("Adding request: " + networkRequest); |
| mConnectivityManager.requestNetwork(networkRequest, networkCallback); |
| sActiveRequests.put( |
| suggestion.getSsid(), Pair.create(networkRequest, networkCallback)); |
| } |
| 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); |
| // untrusted/oem-paid networks need a corresponding NetworkRequest. |
| if (suggestion.isUntrusted() |
| || (SdkLevel.isAtLeastS() |
| && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) { |
| Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc = |
| sActiveRequests.remove(suggestion.getSsid()); |
| 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-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"); |
| mWifiNetworkFactory.setUserApprovedApp(packageName, approved); |
| return 0; |
| } |
| case "network-requests-has-user-approved": { |
| String packageName = getNextArgRequired(); |
| boolean hasUserApproved = mWifiNetworkFactory.hasUserApprovedApp(packageName); |
| pw.println(hasUserApproved ? "yes" : "no"); |
| return 0; |
| } |
| case "set-coex-cell-channels": { |
| if (!SdkLevel.isAtLeastS()) { |
| return handleDefaultCommands(cmd); |
| } |
| mCoexManager.setMockCellChannels(buildCoexCellChannels()); |
| return 0; |
| } |
| case "reset-coex-cell-channels": { |
| if (!SdkLevel.isAtLeastS()) { |
| return handleDefaultCommands(cmd); |
| } |
| mCoexManager.resetMockCellChannels(); |
| return 0; |
| } |
| case "get-coex-cell-channels": { |
| if (!SdkLevel.isAtLeastS()) { |
| return handleDefaultCommands(cmd); |
| } |
| pw.println("Cell channels: " + mCoexManager.getCellChannels()); |
| return 0; |
| } |
| case "set-connected-score": { |
| int score = Integer.parseInt(getNextArgRequired()); |
| CountDownLatch countDownLatch = new CountDownLatch(2); |
| mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer |
| WifiScorer connectedScorer = new WifiScorer(mWifiService, countDownLatch); |
| if (mWifiService.setWifiConnectedNetworkScorer(new Binder(), connectedScorer)) { |
| // wait for retrieving the session id & score observer. |
| countDownLatch.await(1000, TimeUnit.MILLISECONDS); |
| } |
| if (connectedScorer.getSessionId() == null |
| || connectedScorer.getScoreUpdateObserver() == 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: " |
| + connectedScorer.getSessionId()); |
| try { |
| connectedScorer.getScoreUpdateObserver().notifyScoreUpdate( |
| connectedScorer.getSessionId(), 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 "network-suggestions-set-as-carrier-provider": { |
| String packageName = getNextArgRequired(); |
| boolean enabled = getNextArgRequiredTrueOrFalse("yes", "no"); |
| mWifiNetworkSuggestionsManager |
| .setAppWorkingAsCrossCarrierProvider(packageName, enabled); |
| return 0; |
| } |
| case "is-network-suggestions-set-as-carrier-provider": { |
| String packageName = getNextArgRequired(); |
| pw.println(mWifiNetworkSuggestionsManager |
| .isAppWorkingAsCrossCarrierProvider(packageName) ? "yes" : "no"); |
| return 0; |
| } |
| case "remove-shell-app-from-suggestion_database <packageName>": { |
| String packageName = getNextArgRequired(); |
| mWifiNetworkSuggestionsManager.removeApp(packageName); |
| 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; |
| } |
| case "trigger-recovery": { |
| mSelfRecovery.trigger(REASON_API_CALL); |
| return 0; |
| } |
| case "add-fake-scan": { |
| String ssid = getNextArgRequired(); |
| String bssid = getNextArgRequired(); |
| String capabilities = getNextArgRequired(); |
| int frequency; |
| int dbm; |
| String freqStr = getNextArgRequired(); |
| try { |
| frequency = Integer.parseInt(freqStr); |
| } catch (NumberFormatException e) { |
| pw.println( |
| "Invalid frequency argument to 'add-fake-scan' " |
| + "- must be an integer: " + freqStr); |
| return -1; |
| } |
| if (frequency <= 0) { |
| pw.println("Invalid frequency argument to 'add-fake-scan' - must be a " |
| + "positive integer: " + freqStr); |
| } |
| String dbmString = getNextArgRequired(); |
| try { |
| dbm = Integer.parseInt(dbmString); |
| } catch (NumberFormatException e) { |
| pw.println( |
| "Invalid dbm argument to 'add-fake-scan' " |
| + "- must be an integer: " + dbmString); |
| return -1; |
| } |
| ScanResult.InformationElement ieSSid = new ScanResult.InformationElement( |
| ScanResult.InformationElement.EID_SSID, |
| 0, |
| ssid.getBytes(StandardCharsets.UTF_8)); |
| ScanResult.InformationElement[] ies = |
| new ScanResult.InformationElement[]{ieSSid}; |
| ScanDetail sd = new ScanDetail(new NetworkDetail(bssid, ies, null, frequency), |
| WifiSsid.createFromAsciiEncoded(ssid), bssid, capabilities, dbm, |
| frequency, SystemClock.elapsedRealtime() * 1000, ies, null, null); |
| mWifiNative.addFakeScanDetail(sd); |
| return 0; |
| } |
| case "reset-fake-scans": |
| mWifiNative.resetFakeScanDetails(); |
| return 0; |
| case "start-faking-scans": |
| mWifiNative.startFakingScanDetails(); |
| mWifiService.startScan(SHELL_PACKAGE_NAME, null); // to trigger update |
| return 0; |
| case "stop-faking-scans": |
| mWifiNative.stopFakingScanDetails(); |
| return 0; |
| case "enable-scanning": |
| boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); |
| boolean hiddenEnabled = false; |
| String option = getNextOption(); |
| if (option != null) { |
| if (option.equals("-h")) { |
| hiddenEnabled = true; |
| } else { |
| pw.println("Invalid argument to 'enable-scanning' " |
| + "- only allowed option is '-h'"); |
| return -1; |
| } |
| } |
| mScanRequestProxy.enableScanning(enabled, hiddenEnabled); |
| 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 if (option.equals("-r")) { |
| String macRandomizationScheme = getNextArgRequired(); |
| if (macRandomizationScheme.equals("auto")) { |
| configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; |
| } else if (macRandomizationScheme.equals("none")) { |
| configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; |
| } else if (macRandomizationScheme.equals("persistent")) { |
| configuration.macRandomizationSetting = |
| WifiConfiguration.RANDOMIZATION_PERSISTENT; |
| } else if (macRandomizationScheme.equals("non_persistent")) { |
| if (SdkLevel.isAtLeastS()) { |
| configuration.macRandomizationSetting = |
| WifiConfiguration.RANDOMIZATION_NON_PERSISTENT; |
| } else { |
| throw new IllegalArgumentException( |
| "-r non_persistent MAC randomization not supported before S"); |
| } |
| } |
| } else if (option.equals("-h")) { |
| configuration.hiddenSSID = true; |
| } else if (option.equals("-p")) { |
| configuration.shared = false; |
| } 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, "wpa3")) { |
| configBuilder.setPassphrase(getNextArgRequired(), |
| SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); |
| } else if (TextUtils.equals(type, "wpa3_transition")) { |
| configBuilder.setPassphrase(getNextArgRequired(), |
| SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION); |
| } 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_2GHZ |
| | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ); |
| } else if (preferredBand.equals("bridged")) { |
| if (SdkLevel.isAtLeastS()) { |
| int[] dualBands = new int[] { |
| SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ}; |
| configBuilder.setBands(dualBands); |
| } else { |
| throw new IllegalArgumentException( |
| "-b bridged option is not supported before S"); |
| } |
| } 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); |
| } |
| boolean isCarrierMerged = false; |
| String option = getNextOption(); |
| while (option != null) { |
| if (option.equals("-u")) { |
| suggestionBuilder.setUntrusted(true); |
| } else if (option.equals("-o")) { |
| if (SdkLevel.isAtLeastS()) { |
| suggestionBuilder.setOemPaid(true); |
| } else { |
| throw new IllegalArgumentException( |
| "-o OEM paid suggestions not supported before S"); |
| } |
| } else if (option.equals("-p")) { |
| if (SdkLevel.isAtLeastS()) { |
| suggestionBuilder.setOemPrivate(true); |
| } else { |
| throw new IllegalArgumentException( |
| "-p OEM private suggestions not supported before S"); |
| } |
| } 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 if (option.equals("-r")) { |
| if (SdkLevel.isAtLeastS()) { |
| suggestionBuilder.setMacRandomizationSetting( |
| WifiNetworkSuggestion.RANDOMIZATION_NON_PERSISTENT); |
| } else { |
| throw new IllegalArgumentException( |
| "-r non_persistent MAC randomization not supported before S"); |
| } |
| } else if (option.equals("-a")) { |
| if (SdkLevel.isAtLeastS()) { |
| isCarrierMerged = true; |
| } else { |
| throw new IllegalArgumentException("-a option is not supported before S"); |
| } |
| } else if (option.equals("-i")) { |
| if (SdkLevel.isAtLeastS()) { |
| int subId = Integer.parseInt(getNextArgRequired()); |
| suggestionBuilder.setSubscriptionId(subId); |
| } else { |
| throw new IllegalArgumentException( |
| "-i subscription ID option is not supported before S"); |
| } |
| } else if (option.equals("-c")) { |
| int carrierId = Integer.parseInt(getNextArgRequired()); |
| suggestionBuilder.setCarrierId(carrierId); |
| } else if (option.equals("-h")) { |
| suggestionBuilder.setIsHiddenSsid(true); |
| } else { |
| pw.println("Ignoring unknown option " + option); |
| } |
| option = getNextOption(); |
| } |
| WifiNetworkSuggestion suggestion = suggestionBuilder.build(); |
| if (isCarrierMerged) { |
| if (suggestion.wifiConfiguration.subscriptionId |
| == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { |
| pw.println("Carrier merged network must have valid subscription Id"); |
| return null; |
| } |
| suggestion.wifiConfiguration.carrierMerged = true; |
| } |
| return suggestion; |
| } |
| |
| 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, "wpa3_transition")) { |
| 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(); |
| } |
| |
| @NonNull |
| private List<CoexUtils.CoexCellChannel> buildCoexCellChannels() { |
| List<CoexUtils.CoexCellChannel> cellChannels = new ArrayList<>(); |
| while (getRemainingArgsCount() > 0) { |
| final @Annotation.NetworkType int rat; |
| final String ratArg = getNextArgRequired(); |
| if (TextUtils.equals(ratArg, "lte")) { |
| rat = TelephonyManager.NETWORK_TYPE_LTE; |
| } else if (TextUtils.equals(ratArg, "nr")) { |
| rat = TelephonyManager.NETWORK_TYPE_NR; |
| } else { |
| throw new IllegalArgumentException("Unknown rat type " + ratArg); |
| } |
| final int band = Integer.parseInt(getNextArgRequired()); |
| if (band < 1 || band > 261) { |
| throw new IllegalArgumentException("Band is " + band |
| + " but should be a value from 1 to 261"); |
| } |
| final int downlinkFreqKhz = Integer.parseInt(getNextArgRequired()); |
| if (downlinkFreqKhz < 0 && downlinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) { |
| throw new IllegalArgumentException("Downlink frequency is " + downlinkFreqKhz |
| + " but should be >= 0 or UNKNOWN: " |
| + PhysicalChannelConfig.FREQUENCY_UNKNOWN); |
| } |
| final int downlinkBandwidthKhz = Integer.parseInt(getNextArgRequired()); |
| if (downlinkBandwidthKhz <= 0 |
| && downlinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) { |
| throw new IllegalArgumentException("Downlink bandwidth is " + downlinkBandwidthKhz |
| + " but should be > 0 or UNKNOWN: " |
| + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN); |
| } |
| final int uplinkFreqKhz = Integer.parseInt(getNextArgRequired()); |
| if (uplinkFreqKhz < 0 && uplinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) { |
| throw new IllegalArgumentException("Uplink frequency is " + uplinkFreqKhz |
| + " but should be >= 0 or UNKNOWN: " |
| + PhysicalChannelConfig.FREQUENCY_UNKNOWN); |
| } |
| final int uplinkBandwidthKhz = Integer.parseInt(getNextArgRequired()); |
| if (uplinkBandwidthKhz <= 0 |
| && uplinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) { |
| throw new IllegalArgumentException("Uplink bandwidth is " + uplinkBandwidthKhz |
| + " but should be > 0 or UNKNOWN: " |
| + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN); |
| } |
| cellChannels.add(new CoexUtils.CoexCellChannel(rat, band, |
| downlinkFreqKhz, downlinkBandwidthKhz, uplinkFreqKhz, uplinkBandwidthKhz, |
| SubscriptionManager.INVALID_SUBSCRIPTION_ID)); |
| } |
| return cellChannels; |
| } |
| |
| 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); |
| mWifiThreadRunner.post(() -> |
| mActiveModeWarden.getPrimaryClientModeManager().probeLink(new LinkProbeCallback() { |
| @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 " |
| + LinkProbeCallback.failureReasonToString(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(PrintWriter pw, 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); |
| int[] allowed60gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_60_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]; |
| } |
| if (allowed60gFreq == null) { |
| allowed60gFreq = new int[0]; |
| } |
| pw.println("2G freq: " + Arrays.toString(allowed2gFreq)); |
| pw.println("5G freq: " + Arrays.toString(allowed5gFreq)); |
| pw.println("5G DFS: " + Arrays.toString(allowed5gDfsFreq)); |
| pw.println("6G freq: " + Arrays.toString(allowed6gFreq)); |
| pw.println("60G freq: " + Arrays.toString(allowed60gFreq)); |
| return (Arrays.binarySearch(allowed2gFreq, apChannelMHz) >= 0 |
| || Arrays.binarySearch(allowed5gFreq, apChannelMHz) >= 0 |
| || Arrays.binarySearch(allowed5gDfsFreq, apChannelMHz) >= 0) |
| || Arrays.binarySearch(allowed6gFreq, apChannelMHz) >= 0 |
| || Arrays.binarySearch(allowed60gFreq, apChannelMHz) >= 0; |
| } |
| |
| private void waitForWifiEnabled(boolean enabled) throws InterruptedException { |
| CountDownLatch countDownLatch = new CountDownLatch(1); |
| BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { |
| int state = mWifiService.getWifiEnabledState(); |
| if ((enabled && state == WIFI_STATE_ENABLED) |
| || (!enabled && state == WIFI_STATE_DISABLED)) { |
| countDownLatch.countDown(); |
| } |
| } |
| } |
| }; |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); |
| mContext.registerReceiver(broadcastReceiver, filter); |
| mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled); |
| countDownLatch.await(5000, TimeUnit.MILLISECONDS); |
| mContext.unregisterReceiver(broadcastReceiver); |
| } |
| |
| private void printWifiInfo(PrintWriter pw, WifiInfo info) { |
| 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()); |
| } |
| |
| 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; |
| } |
| if (Binder.getCallingUid() != Process.ROOT_UID) { |
| // not privileged, just dump the primary client mode manager manager status |
| // (public API contents). |
| pw.println("==== Primary ClientModeManager instance ===="); |
| printWifiInfo(pw, mWifiService.getConnectionInfo(SHELL_PACKAGE_NAME, null)); |
| } else { |
| // privileged, dump out all the client mode manager manager statuses |
| for (ClientModeManager cm : mActiveModeWarden.getClientModeManagers()) { |
| pw.println("==== ClientModeManager instance: " + cm + " ===="); |
| WifiInfo info = cm.syncRequestConnectionInfo(); |
| printWifiInfo(pw, info); |
| if (info.getSupplicantState() != SupplicantState.COMPLETED) { |
| continue; |
| } |
| Network network = cm.syncGetCurrentNetwork(); |
| NetworkCapabilities capabilities = |
| mConnectivityManager.getNetworkCapabilities(network); |
| pw.println("NetworkCapabilities: " + capabilities); |
| } |
| } |
| } |
| |
| 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>] [-r auto|none|persistent|non_persistent]"); |
| 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(" -h - Mark the network hidden."); |
| pw.println(" -p - Mark the network private (not shared)."); |
| pw.println(" -b <bssid> - Set specific BSSID."); |
| pw.println(" -r auto|none|persistent|non_persistent - MAC randomization scheme for the" |
| + " network"); |
| pw.println(" add-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-m] [-d] " |
| + "[-b <bssid>] [-r auto|none|persistent|non_persistent]"); |
| 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(" -h - Mark the network hidden."); |
| pw.println(" -p - Mark the network private (not shared)."); |
| pw.println(" -b <bssid> - Set specific BSSID."); |
| pw.println(" -r auto|none|persistent|non_persistent - MAC randomization scheme for the" |
| + " network"); |
| 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(" is-verbose-logging"); |
| pw.println(" Check whether verbose logging enabled or disabled"); |
| pw.println(" start-restricting-auto-join-to-subscription-id subId"); |
| pw.println(" temporarily disable all wifi networks except merged carrier networks with" |
| + " the given subId"); |
| pw.println(" stop-restricting-auto-join-to-subscription-id"); |
| pw.println(" Undo the effects of " |
| + "start-restricting-auto-join-to-subscription-id"); |
| pw.println(" add-suggestion <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-u] [-o] [-p] [-m] " |
| + " [-s] [-d] [-b <bssid>] [-e] [-i] [-a <carrierId>] [-c <subscriptionId>]"); |
| 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(" -o - Mark the suggestion oem paid."); |
| pw.println(" -p - Mark the suggestion oem private."); |
| pw.println(" -m - Mark the suggestion metered."); |
| pw.println(" -h - Mark the network hidden."); |
| 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(" -r - Enable non_persistent randomization (disabled by default)"); |
| pw.println(" -a - Mark the suggestion carrier merged"); |
| pw.println(" -c <carrierId> - set carrier Id"); |
| pw.println(" -i <subscriptionId> - set subscription Id, if -a is used, " |
| + "this must be set"); |
| 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"); |
| if (SdkLevel.isAtLeastS()) { |
| pw.println(" set-coex-cell-channels [lte|nr <bandNumber 1-261> " |
| + "<downlinkFreqKhz or UNKNOWN: " |
| + PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> " |
| + "<downlinkBandwidthKhz or UNKNOWN: " |
| + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + "> " |
| + "<uplinkFreqKhz or UNKNOWN: " |
| + PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> " |
| + "<uplinkBandwidthKhz or UNKNOWN: " |
| + PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + ">] ..."); |
| pw.println(" Sets a list of zero or more cell channels to use for coex calculations." |
| + " Actual device reported cell channels will be ignored until" |
| + " reset-coex-cell-channels is called."); |
| pw.println(" reset-coex-cell-channels"); |
| pw.println(" Removes all cell channels set in set-coex-cell-channels and returns to " |
| + "listening on actual device reported cell channels"); |
| pw.println(" get-coex-cell-channels"); |
| pw.println(" Prints the cell channels being used for coex."); |
| } |
| 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|wpa3|wpa3_transition) <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|wpa3|wpa3_transition - Security type of the network."); |
| pw.println(" - Use 'open' for networks with no passphrase"); |
| pw.println(" - Use 'wpa2', 'wpa3', 'wpa3_transition' for networks with passphrase"); |
| pw.println(" -b 2|5|6|any|bridged - 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(" - Use 'bridged' to indicate bridged AP which enables APs on both " |
| + "2.4G + 5G"); |
| 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)"); |
| pw.println(" pmksa-flush <networkId>"); |
| pw.println(" - Flush the local PMKSA cache associated with the network id." |
| + " Use list-networks to retrieve <networkId> for the network"); |
| } |
| |
| 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-band enabled <int> | disabled"); |
| pw.println(" Forces soft AP band to 2|5|6"); |
| 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',"); |
| pw.println(" and/or 'wifi_softap_bridged_ap_supported',"); |
| pw.println(" and/or 'wifi_softap_bridged_ap_with_sta_supported',"); |
| pw.println(" 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."); |
| pw.println(" network-suggestions-set-as-carrier-provider <packageName> yes|no"); |
| pw.println(" Set the <packageName> work as carrier provider or not."); |
| pw.println(" is-network-suggestions-set-as-carrier-provider <packageName>"); |
| pw.println(" Queries whether the <packageName> is working as carrier provider or not."); |
| pw.println(" remove-app-from-suggestion_database <packageName>"); |
| pw.println(" Remove <packageName> from the suggestion database, all suggestions and user" |
| + " approval will be deleted, it is the same as uninstalling this app."); |
| pw.println(" trigger-recovery"); |
| pw.println(" Trigger Wi-Fi subsystem restart."); |
| pw.println(" start-faking-scans"); |
| pw.println(" Start faking scan results into the framework (configured with " |
| + "'add-fake-scan'), stop with 'stop-faking-scans'."); |
| pw.println(" stop-faking-scans"); |
| pw.println(" Stop faking scan results - started with 'start-faking-scans'."); |
| pw.println(" add-fake-scan <ssid> <bssid> <capabilities> <frequency> <dbm>"); |
| pw.println(" Add a fake scan result to be used when enabled via `start-faking-scans'."); |
| pw.println(" Example WPA2: add-fake-scan fakeWpa2 80:01:02:03:04:05 " |
| + "\"[WPA2-PSK-CCMP][RSN-PSK-CCMP][ESS]\" 2412 -55"); |
| pw.println(" Example WPA3: add-fake-scan fakeWpa3 80:01:02:03:04:06 " |
| + "\"[RSN-SAE+FT/SAE-CCMP][ESS]\" 2412 -55"); |
| pw.println( |
| " Example Open: add-fake-scan fakeOpen 80:01:02:03:04:07 \"[ESS]\" 2412 -55"); |
| pw.println(" Example OWE: add-fake-scan fakeOwe 80:01:02:03:04:08 \"[RSN-OWE-CCMP]\" " |
| + "2412 -55"); |
| pw.println( |
| " Example WPA2/WPA3 transition mode: add-fake-scan fakeWpa2t3 80:01:02:03:04:09 " |
| + "\"[WPA2-PSK-CCMP][RSN-PSK+SAE-CCMP][ESS][MFPC]\" 2412 -55"); |
| pw.println( |
| " Example Open/OWE transition mode: add-fake-scan fakeOpenOwe 80:01:02:03:04:0A " |
| + "\"[RSN-OWE_TRANSITION-CCMP][ESS]\" 2412 -55"); |
| pw.println( |
| " Example Passpoint: add-fake-scan fakePasspoint 80:01:02:03:04:0B " |
| + "\"[WPA2-EAP/SHA1-CCMP][RSN-EAP/SHA1-CCMP][ESS][MFPR][MFPC]" |
| + "[PASSPOINT]\" 2412 -55"); |
| pw.println(" reset-fake-scans"); |
| pw.println(" Resets all fake scan results added by 'add-fake-scan'."); |
| pw.println(" enable-scanning enabled|disabled [-h]"); |
| pw.println(" Sets whether all scanning should be enabled or disabled"); |
| pw.println(" -h - Enable scanning for hidden networks."); |
| } |
| |
| @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(s)"); |
| for (WifiNetworkSuggestion suggestion : suggestions) { |
| pw.println(String.format("%-32s %-4s", |
| WifiInfo.sanitizeSsid(suggestion.getWifiConfiguration().SSID), |
| suggestion.getWifiConfiguration().getSecurityParamsList().stream() |
| .map(p -> WifiConfiguration.getSecurityTypeName( |
| p.getSecurityType()) |
| + (p.isAddedByAutoUpgrade() ? "^" : "")) |
| .collect(Collectors.joining("/")))); |
| } |
| } |
| } |
| } |