/* * 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.ACTION_REMOVE_SUGGESTION_DISCONNECT; import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_LINGER; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED; import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED; import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_NAN; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_P2P; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA; 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.hardware.display.DisplayManager; import android.location.Location; import android.location.LocationManager; 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.IDppCallback; import android.net.wifi.ILastCallerListener; import android.net.wifi.ILocalOnlyHotspotCallback; import android.net.wifi.IPnoScanResultsCallback; 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.SoftApState; import android.net.wifi.SupplicantState; import android.net.wifi.WifiAvailableChannel; import android.net.wifi.WifiClient; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConnectedSessionInfo; import android.net.wifi.WifiContext; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkSelectionConfig; import android.net.wifi.WifiNetworkSpecifier; import android.net.wifi.WifiNetworkSuggestion; import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; import android.net.wifi.util.ScanResultUtil; import android.net.wifi.util.WifiResourceCache; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.WorkSource; import android.telephony.Annotation; import android.telephony.PhysicalChannelConfig; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.Display; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; 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.hal.WifiChip; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.util.ApConfigUtil; import com.android.server.wifi.util.ArrayUtils; import libcore.util.HexEncoding; import java.io.PrintWriter; import java.nio.charset.Charset; 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.HashMap; 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 "" 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", "forget-network", "connect-network", "add-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", "set-passpoint-enabled", "set-multi-internet-state", "start-scan", "status", "query-interface", "interface-priority-interactive-mode", "set-one-shot-screen-on-delay-ms", "set-network-selection-config", "set-ipreach-disconnect", "get-ipreach-disconnect", "take-bugreport", "get-allowed-channel", "set-mock-wifimodem-service", "get-mock-wifimodem-service", "set-mock-wifimodem-methods", "force-overlay-config-value", "get-softap-supported-features", "get-overlay-config-values" }; private static final Map> 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 WifiContext 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; private final @NonNull WifiDialogManager mWifiDialogManager; private final HalDeviceManager mHalDeviceManager; private final InterfaceConflictManager mInterfaceConflictManager; private final SsidTranslator mSsidTranslator; private final WifiDiagnostics mWifiDiagnostics; private final DeviceConfigFacade mDeviceConfig; private final AfcManager mAfcManager; private final WifiInjector mWifiInjector; private static final int[] OP_MODE_LIST = { WifiAvailableChannel.OP_MODE_STA, WifiAvailableChannel.OP_MODE_SAP, WifiAvailableChannel.OP_MODE_WIFI_DIRECT_CLI, WifiAvailableChannel.OP_MODE_WIFI_DIRECT_GO, WifiAvailableChannel.OP_MODE_WIFI_AWARE, WifiAvailableChannel.OP_MODE_TDLS, }; private class SoftApCallbackProxy extends ISoftApCallback.Stub { private final PrintWriter mPrintWriter; private final CountDownLatch mCountDownLatch; SoftApCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch) { mPrintWriter = printWriter; mCountDownLatch = countDownLatch; } @Override public void onStateChanged(SoftApState state) { mPrintWriter.println("onStateChanged with state: " + state); mSapState = state.getState(); if (mSapState == WifiManager.WIFI_AP_STATE_ENABLED) { mPrintWriter.println(" SAP is enabled successfully"); // Skip countDown() and wait for onInfoChanged() which has // the confirmed softAp channel information } else if (mSapState == WifiManager.WIFI_AP_STATE_DISABLED) { mPrintWriter.println(" SAP is disabled"); } else if (mSapState == WifiManager.WIFI_AP_STATE_FAILED) { mPrintWriter.println(" SAP failed to start"); mCountDownLatch.countDown(); } } @Override public void onConnectedClientsOrInfoChanged(Map infos, Map> clients, boolean isBridged, boolean isRegistration) { mPrintWriter.println("onConnectedClientsOrInfoChanged, infos: " + infos + ", clients: " + clients + ", isBridged: " + isBridged); if (mSapState == WifiManager.WIFI_AP_STATE_ENABLED && infos.size() != 0) { mCountDownLatch.countDown(); } } @Override public void onCapabilityChanged(SoftApCapability capability) { mPrintWriter.println("onCapabilityChanged " + capability); } @Override public void onBlockedClientConnecting(WifiClient client, int reason) { } } /** * Used for shell command testing of DPP feature. */ public static class DppCallbackProxy extends IDppCallback.Stub { private final PrintWriter mPrintWriter; private final CountDownLatch mCountDownLatch; private static final int STATUS_SUCCESS = 0; private static final int STATUS_PROGRESS = 1; private static final int STATUS_FAILURE = 2; DppCallbackProxy(PrintWriter printWriter, CountDownLatch countDownLatch) { mPrintWriter = printWriter; mCountDownLatch = countDownLatch; } @Override public void onSuccessConfigReceived(int networkId) { mPrintWriter.println("onSuccessConfigReceived. netId=" + networkId); mCountDownLatch.countDown(); } @Override public void onSuccess(int status) { mPrintWriter.println("onSuccess status=" + statusToString(STATUS_SUCCESS, status)); mCountDownLatch.countDown(); } @Override public void onFailure(int status, String ssid, String channelList, int[] bandArray) { mPrintWriter.println("onFailure. status=" + statusToString(STATUS_FAILURE, status) + "ssid=" + ssid + "channelList=" + channelList); mCountDownLatch.countDown(); } @Override public void onProgress(int status) { mPrintWriter.println("onProgress status=" + statusToString(STATUS_PROGRESS, status)); } @Override public void onBootstrapUriGenerated(String uri) { mPrintWriter.println("onBootstrapUriGenerated URI = " + uri); } private String statusToString(int type, int status) { switch (type) { case STATUS_SUCCESS: { switch (status) { case 0: return "CONFIGURATION_SENT"; case 1: return "CONFIGURATION_APPLIED"; default: return "Unknown success code"; } } case STATUS_PROGRESS: { switch (status) { case 0: return "AUTHENTICATION_SUCCESS"; case 1: return "RESPONSE_PENDING"; case 2: return "CONFIGURATION_SENT_WAITING_RESPONSE"; case 3: return "CONFIGURATION_ACCEPTED"; default: return "Unknown progress code"; } } case STATUS_FAILURE: { switch (status) { case -1: return "INVALID_URI"; case -2: return "AUTHENTICATION"; case -3: return "NOT_COMPATIBLE"; case -4: return "CONFIGURATION"; case -5: return "BUSY"; case -6: return "TIMEOUT"; case -7: return "GENERIC"; case -8: return "NOT_SUPPORTED"; case -9: return "INVALID_NETWORK"; case -10: return "CANNOT_FIND_NETWORK"; case -11: return "ENROLLEE_AUTHENTICATION"; case -12: return "ENROLLEE_REJECTED_CONFIGURATION"; case -13: return "URI_GENERATION"; case -14: return "ENROLLEE_FAILED_TO_SCAN_NETWORK_CHANNEL"; default: return "Unknown failure code"; } } default : return "Unknown status type"; } } } /** * 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(); } @Override public void onNetworkSwitchAccepted( int sessionId, int targetNetworkId, String targetBssid) { Log.i(TAG, "onNetworkSwitchAccepted:" + " sessionId=" + sessionId + " targetNetworkId=" + targetNetworkId + " targetBssid=" + targetBssid); } @Override public void onNetworkSwitchRejected( int sessionId, int targetNetworkId, String targetBssid) { Log.i(TAG, "onNetworkSwitchRejected:" + " sessionId=" + sessionId + " targetNetworkId=" + targetNetworkId + " targetBssid=" + targetBssid); } public Integer getSessionId() { return mSessionId; } public IScoreUpdateObserver getScoreUpdateObserver() { return mScoreUpdateObserver; } } WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, WifiContext context, WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner) { mWifiInjector = wifiInjector; 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(); mWifiDialogManager = wifiInjector.getWifiDialogManager(); mHalDeviceManager = wifiInjector.getHalDeviceManager(); mInterfaceConflictManager = wifiInjector.getInterfaceConflictManager(); mSsidTranslator = wifiInjector.getSsidTranslator(); mWifiDiagnostics = wifiInjector.getWifiDiagnostics(); mDeviceConfig = wifiInjector.getDeviceConfigFacade(); mAfcManager = wifiInjector.getAfcManager(); } private String getOpModeName(@WifiAvailableChannel.OpMode int mode) { switch (mode) { case WifiAvailableChannel.OP_MODE_STA: return "STA"; case WifiAvailableChannel.OP_MODE_SAP: return "SAP"; case WifiAvailableChannel.OP_MODE_WIFI_DIRECT_CLI: return "WiFi-Direct GC"; case WifiAvailableChannel.OP_MODE_WIFI_DIRECT_GO: return "WiFi-Direct GO"; case WifiAvailableChannel.OP_MODE_WIFI_AWARE: return "WiFi-Aware"; case WifiAvailableChannel.OP_MODE_TDLS: return "TDLS"; default: return ""; } } @Override public int onCommand(String cmd) { // Treat no command as help command. if (TextUtils.isEmpty(cmd)) { 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"); mWifiThreadRunner.post(() -> mWifiNetworkSuggestionsManager .setHasUserApprovedForApp(approved, Binder.getCallingUid(), packageName), "shell#setHasUserApprovedForApp"); 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(); try { final int carrierId = Integer.parseInt(arg1); mWifiThreadRunner.post(() -> mWifiCarrierInfoManager.clearImsiPrivacyExemptionForCarrier( carrierId), TAG + "#" + cmd); } catch (NumberFormatException e) { pw.println("Invalid argument to " + "'imsi-protection-exemption-clear-user-approved-for-carrier' " + "- 'carrierId' must be an Integer"); return -1; } return 0; } case "network-requests-remove-user-approved-access-points": { String packageName = getNextArgRequired(); mWifiThreadRunner.post(() -> mWifiNetworkFactory.removeApp(packageName), TAG + "#" + cmd); return 0; } case "clear-user-disabled-networks": { mWifiConfigManager.clearUserTemporarilyDisabledList(); return 0; } case "send-link-probe": { return sendLinkProbe(pw); } case "get-last-caller-info": { int apiType = Integer.parseInt(getNextArgRequired()); mWifiService.getLastCallerInfoForApi(apiType, new ILastCallerListener.Stub() { @Override public void onResult(String packageName, boolean enabled) { Log.i(TAG, "getLastCallerInfoForApi " + apiType + ": packageName=" + packageName + ", enabled=" + enabled); } }); return 0; } case "force-softap-band": { boolean forceBandEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); if (forceBandEnabled) { String forcedBand = getNextArgRequired(); if (forcedBand.equals("2")) { mWifiApConfigStore.enableForceSoftApBandOrChannel( SoftApConfiguration.BAND_2GHZ, 0, SoftApInfo.CHANNEL_WIDTH_AUTO); } else if (forcedBand.equals("5")) { mWifiApConfigStore.enableForceSoftApBandOrChannel( SoftApConfiguration.BAND_5GHZ, 0, SoftApInfo.CHANNEL_WIDTH_AUTO); } else if (forcedBand.equals("6")) { mWifiApConfigStore.enableForceSoftApBandOrChannel( SoftApConfiguration.BAND_6GHZ, 0, SoftApInfo.CHANNEL_WIDTH_AUTO); } 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; int apMaxBandWidthMHz = 0; try { apChannelMHz = Integer.parseInt(getNextArgRequired()); String option = getNextOption(); if (option != null && option.equals("-w")) { if (!SdkLevel.isAtLeastT()) { pw.println("Maximum channel bandwidth can be set only on" + " SdkLevel T or later."); return -1; } String bandwidthStr = getNextArgRequired(); try { apMaxBandWidthMHz = Integer.parseInt(bandwidthStr); } catch (NumberFormatException e) { pw.println("Invalid maximum channel bandwidth arg: " + bandwidthStr); return -1; } } } 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); int apMaxBandWidth; switch (apMaxBandWidthMHz) { case 0: apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_AUTO; break; case 20: apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_20MHZ; break; case 40: apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_40MHZ; break; case 80: apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_80MHZ; break; case 160: apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_160MHZ; break; case 320: apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_320MHZ; break; default: pw.println("Invalid max channel bandwidth " + apMaxBandWidthMHz); return -1; } pw.println("channel: " + apChannel + " band: " + band + " maximum channel bandwidth: " + apMaxBandWidthMHz); 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, apMaxBandWidth); return 0; } else { mWifiApConfigStore.disableForceSoftApBandOrChannel(); return 0; } } case "set-pno-request": { if (!SdkLevel.isAtLeastT()) { pw.println("This feature is only supported on SdkLevel T or later."); return -1; } String ssid = getNextArgRequired(); int frequency = -1; WifiSsid wifiSsid = WifiSsid.fromString("\"" + ssid + "\""); String option = getNextOption(); if (option != null) { if (option.equals("-f")) { frequency = Integer.parseInt(getNextArgRequired()); } else { pw.println("Invalid argument to 'set-pno-request' " + "- only allowed option is '-f'"); return -1; } } int[] frequencies = frequency == -1 ? new int[0] : new int[] {frequency}; IPnoScanResultsCallback.Stub callback = new IPnoScanResultsCallback.Stub() { @Override public void onScanResultsAvailable(List scanResults) { Log.v(TAG, "PNO scan results available:"); for (ScanResult result : scanResults) { Log.v(TAG, result.getWifiSsid().toString()); } } @Override public void onRegisterSuccess() { Log.v(TAG, "PNO scan request register success"); } @Override public void onRegisterFailed(int reason) { Log.v(TAG, "PNO scan request register failed reason=" + reason); } @Override public void onRemoved(int reason) { Log.v(TAG, "PNO scan request callback removed reason=" + reason); } }; pw.println("requesting PNO scan for: " + wifiSsid); mWifiService.setExternalPnoScanRequest(new Binder(), callback, Arrays.asList(wifiSsid), frequencies, mContext.getOpPackageName(), mContext.getAttributionTag()); return 0; } case "clear-pno-request": { if (!SdkLevel.isAtLeastT()) { pw.println("This feature is only supported on SdkLevel T or later."); return -1; } mWifiService.clearExternalPnoScanRequest(); return 0; } case "start-lohs": { CountDownLatch countDownLatch = new CountDownLatch(2); SoftApConfiguration config = buildSoftApConfiguration(pw); ILocalOnlyHotspotCallback.Stub lohsCallback = new ILocalOnlyHotspotCallback.Stub() { @Override public void onHotspotStarted(SoftApConfiguration config) { pw.println("Lohs onStarted, config = " + config); countDownLatch.countDown(); } @Override public void onHotspotStopped() { pw.println("Lohs onStopped"); countDownLatch.countDown(); } @Override public void onHotspotFailed(int reason) { pw.println("Lohs onFailed: " + reason); countDownLatch.countDown(); } }; SoftApCallbackProxy softApCallback = new SoftApCallbackProxy(pw, countDownLatch); Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mWifiService.registerLocalOnlyHotspotSoftApCallback(softApCallback, extras); if (REQUEST_REGISTERED != mWifiService.startLocalOnlyHotspot( lohsCallback, SHELL_PACKAGE_NAME, null /* featureId */, config, extras)) { pw.println("Lohs failed to start. Please check config parameters"); } // Wait for lohs to start and complete callback countDownLatch.await(10000, TimeUnit.MILLISECONDS); mWifiService.unregisterLocalOnlyHotspotSoftApCallback(softApCallback, extras); return 0; } case "start-softap": { CountDownLatch countDownLatch = new CountDownLatch(1); SoftApConfiguration config = buildSoftApConfiguration(pw); SoftApCallbackProxy softApCallback = new SoftApCallbackProxy(pw, countDownLatch); mWifiService.registerSoftApCallback(softApCallback); 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(10000, TimeUnit.MILLISECONDS); mWifiService.unregisterSoftApCallback(softApCallback); return 0; } case "stop-lohs": { mWifiService.stopLocalOnlyHotspot(); pw.println("Lohs stopped successfully"); 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 "reload-resources": { mContext.resetResourceCache(); 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-passpoint-enabled": { boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); mWifiService.setWifiPasspointEnabled(enabled); return 0; } case "set-multi-internet-mode": { int mode = Integer.parseInt(getNextArgRequired()); mWifiService.setStaConcurrencyForMultiInternetMode(mode); 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 scanResults = mScanRequestProxy.getScanResults(); 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 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, SHELL_PACKAGE_NAME, new Bundle()); 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, SHELL_PACKAGE_NAME); 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"); String levelOption = getNextOption(); int level = enabled ? 1 : 0; if (enabled && levelOption != null && levelOption.equals("-l")) { String levelStr = getNextArgRequired(); try { level = Integer.parseInt(levelStr); if (level < VERBOSE_LOGGING_LEVEL_DISABLED || level > VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY) { pw.println("Not a valid log level: " + level); return -1; } } catch (NumberFormatException e) { pw.println("Invalid verbose-logging level : " + levelStr); return -1; } } mWifiService.enableVerboseLogging(level); 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(); String action = getNextArg(); int actionCode = ACTION_REMOVE_SUGGESTION_DISCONNECT; if (action != null && action.equals("lingering")) { actionCode = ACTION_REMOVE_SUGGESTION_LINGER; } List 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, actionCode); // untrusted/oem-paid networks need a corresponding NetworkRequest. if (suggestion.isUntrusted() || (SdkLevel.isAtLeastS() && (suggestion.isOemPaid() || suggestion.isOemPrivate()))) { Pair 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, WifiManager.ACTION_REMOVE_SUGGESTION_DISCONNECT); return 0; case "list-suggestions": { List suggestions = mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME); printWifiNetworkSuggestions(pw, suggestions); return 0; } case "list-all-suggestions": { Set suggestions = mWifiNetworkSuggestionsManager.getAllNetworkSuggestions(); printWifiNetworkSuggestions(pw, suggestions); return 0; } case "list-suggestions-from-app": { String packageName = getNextArgRequired(); List suggestions = mWifiService.getNetworkSuggestions(packageName); printWifiNetworkSuggestions(pw, suggestions); return 0; } case "allow-root-to-get-local-only-cmm": { boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); mActiveModeWarden.allowRootToGetLocalOnlyCmm(enabled); return 0; } case "add-request": { Pair result = buildNetworkRequest(pw); String ssid = result.first; NetworkRequest networkRequest = result.second; ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback(); pw.println("Adding request: " + networkRequest); mWifiThreadRunner.post(() -> mConnectivityManager .requestNetwork(networkRequest, networkCallback), "shell#add-request"); sActiveRequests.put(ssid, Pair.create(networkRequest, networkCallback)); return 0; } case "remove-request": { String ssid = getNextArgRequired(); Pair nrAndNc = sActiveRequests.remove(ssid); if (nrAndNc == null) { pw.println("No matching request to remove"); return -1; } pw.println("Removing request: " + nrAndNc.first); mWifiThreadRunner.post(() -> mConnectivityManager .unregisterNetworkCallback(nrAndNc.second), "shell#remove-request"); return 0; } case "remove-all-requests": if (sActiveRequests.isEmpty()) { pw.println("No active requests"); return -1; } for (Pair nrAndNc : sActiveRequests.values()) { pw.println("Removing request: " + nrAndNc.first); mWifiThreadRunner.post(() -> mConnectivityManager.unregisterNetworkCallback(nrAndNc.second), "shell#remove-request"); } sActiveRequests.clear(); return 0; case "list-requests": if (sActiveRequests.isEmpty()) { pw.println("No active requests"); } else { pw.println("SSID NetworkRequest"); for (Map.Entry> 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 ": { 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 "set-emergency-scan-request": { boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); mWifiService.setEmergencyScanRequestInProgress(enabled); return 0; } case "trigger-recovery": { mSelfRecovery.trigger(REASON_API_CALL); return 0; } case "add-fake-scan": { String option = getNextOption(); boolean isHex = (option != null && option.equals("-x")); WifiSsid wifiSsid = WifiSsid.fromBytes(isHex ? HexEncoding.decode(getNextArgRequired()) : getNextArgRequired().getBytes(StandardCharsets.UTF_8)); 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, wifiSsid.getBytes()); ScanResult.InformationElement[] ies = new ScanResult.InformationElement[]{ieSSid}; ScanDetail sd = new ScanDetail(new NetworkDetail(bssid, ies, null, frequency), wifiSsid, 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; } case "launch-dialog-simple": String title = null; String message = null; String messageUrl = null; int messageUrlStart = 0; int messageUrlEnd = 0; String positiveButtonText = null; String negativeButtonText = null; String neutralButtonText = null; String dialogOption = getNextOption(); boolean simpleTimeoutSpecified = false; long simpleTimeoutMs = 0; boolean useLegacy = false; while (dialogOption != null) { switch (dialogOption) { case "-t": title = getNextArgRequired(); break; case "-m": message = getNextArgRequired(); break; case "-l": messageUrl = getNextArgRequired(); messageUrlStart = Integer.valueOf(getNextArgRequired()); messageUrlEnd = Integer.valueOf(getNextArgRequired()); break; case "-y": positiveButtonText = getNextArgRequired(); break; case "-n": negativeButtonText = getNextArgRequired(); break; case "-x": neutralButtonText = getNextArgRequired(); break; case "-c": simpleTimeoutMs = Integer.parseInt(getNextArgRequired()); simpleTimeoutSpecified = true; break; case "-s": useLegacy = true; break; default: pw.println("Ignoring unknown option " + dialogOption); break; } dialogOption = getNextOption(); } ArrayBlockingQueue simpleQueue = new ArrayBlockingQueue<>(1); WifiDialogManager.SimpleDialogCallback wifiEnableRequestCallback = new WifiDialogManager.SimpleDialogCallback() { @Override public void onPositiveButtonClicked() { simpleQueue.offer("Positive button was clicked."); } @Override public void onNegativeButtonClicked() { simpleQueue.offer("Negative button was clicked."); } @Override public void onNeutralButtonClicked() { simpleQueue.offer("Neutral button was clicked."); } @Override public void onCancelled() { simpleQueue.offer("Dialog was cancelled."); } }; WifiDialogManager.DialogHandle simpleDialogHandle; if (useLegacy) { simpleDialogHandle = mWifiDialogManager.createLegacySimpleDialogWithUrl( title, message, messageUrl, messageUrlStart, messageUrlEnd, positiveButtonText, negativeButtonText, neutralButtonText, wifiEnableRequestCallback, mWifiThreadRunner); } else { simpleDialogHandle = mWifiDialogManager.createSimpleDialogWithUrl( title, message, messageUrl, messageUrlStart, messageUrlEnd, positiveButtonText, negativeButtonText, neutralButtonText, wifiEnableRequestCallback, mWifiThreadRunner); } if (simpleTimeoutSpecified) { simpleDialogHandle.launchDialog(simpleTimeoutMs); pw.println("Launched dialog with " + simpleTimeoutMs + " millisecond" + " timeout. Waiting for user response..."); pw.flush(); String dialogResponse = simpleQueue.take(); if (dialogResponse == null) { pw.println("No response received."); } else { pw.println(dialogResponse); } } else { simpleDialogHandle.launchDialog(); pw.println("Launched dialog. Waiting up to 15 seconds for user response" + " before dismissing..."); pw.flush(); String dialogResponse = simpleQueue.poll(15, TimeUnit.SECONDS); if (dialogResponse == null) { pw.println("No response received. Dismissing dialog."); simpleDialogHandle.dismissDialog(); } else { pw.println(dialogResponse); } } return 0; case "launch-dialog-p2p-invitation-sent": { int displayId = Display.DEFAULT_DISPLAY; String deviceName = getNextArgRequired(); String displayPin = getNextArgRequired(); String cmdOption = getNextOption(); if (cmdOption != null && cmdOption.equals("-i")) { String displayIdStr = getNextArgRequired(); try { displayId = Integer.parseInt(displayIdStr); } catch (NumberFormatException e) { pw.println("Invalid argument to " + "'launch-dialog-p2p-invitation-sent' " + "- must be an integer: " + displayIdStr); return -1; } DisplayManager dm = mContext.getSystemService(DisplayManager.class); Display[] displays = dm.getDisplays(); for (Display display : displays) { pw.println("Display: id=" + display.getDisplayId() + ", info=" + display.getDeviceProductInfo()); } } mWifiDialogManager.createP2pInvitationSentDialog(deviceName, displayPin, displayId).launchDialog(); pw.println("Launched dialog."); return 0; } case "launch-dialog-p2p-invitation-received": { String deviceName = getNextArgRequired(); boolean isPinRequested = false; String displayPin = null; String pinOption = getNextOption(); int displayId = Display.DEFAULT_DISPLAY; boolean p2pInvRecTimeoutSpecified = false; long p2pInvRecTimeout = 0; while (pinOption != null) { if (pinOption.equals("-p")) { isPinRequested = true; } else if (pinOption.equals("-d")) { displayPin = getNextArgRequired(); } else if (pinOption.equals("-i")) { String displayIdStr = getNextArgRequired(); try { displayId = Integer.parseInt(displayIdStr); } catch (NumberFormatException e) { pw.println("Invalid argument to " + "'launch-dialog-p2p-invitation-received' " + "- must be an integer: " + displayIdStr); return -1; } DisplayManager dm = mContext.getSystemService(DisplayManager.class); Display[] displays = dm.getDisplays(); for (Display display : displays) { pw.println("Display: id=" + display.getDisplayId() + ", info=" + display.getDeviceProductInfo()); } } else if (pinOption.equals("-c")) { p2pInvRecTimeout = Integer.parseInt(getNextArgRequired()); p2pInvRecTimeoutSpecified = true; } else { pw.println("Ignoring unknown option " + pinOption); } pinOption = getNextOption(); } ArrayBlockingQueue p2pInvRecQueue = new ArrayBlockingQueue<>(1); WifiDialogManager.P2pInvitationReceivedDialogCallback callback = new WifiDialogManager.P2pInvitationReceivedDialogCallback() { @Override public void onAccepted(@Nullable String optionalPin) { p2pInvRecQueue.offer("Invitation accepted with optionalPin=" + optionalPin); } @Override public void onDeclined() { p2pInvRecQueue.offer("Invitation declined"); } }; WifiDialogManager.DialogHandle p2pInvitationReceivedDialogHandle = mWifiDialogManager.createP2pInvitationReceivedDialog( deviceName, isPinRequested, displayPin, displayId, callback, mWifiThreadRunner); if (p2pInvRecTimeoutSpecified) { p2pInvitationReceivedDialogHandle.launchDialog(p2pInvRecTimeout); pw.println("Launched dialog with " + p2pInvRecTimeout + " millisecond" + " timeout. Waiting for user response..."); pw.flush(); String dialogResponse = p2pInvRecQueue.take(); if (dialogResponse == null) { pw.println("No response received."); } else { pw.println(dialogResponse); } } else { p2pInvitationReceivedDialogHandle.launchDialog(); pw.println("Launched dialog. Waiting up to 15 seconds for user response" + " before dismissing..."); pw.flush(); String dialogResponse = p2pInvRecQueue.poll(15, TimeUnit.SECONDS); if (dialogResponse == null) { pw.println("No response received. Dismissing dialog."); p2pInvitationReceivedDialogHandle.dismissDialog(); } else { pw.println(dialogResponse); } } return 0; } case "query-interface": { String uidArg = getNextArgRequired(); int uid = 0; try { uid = Integer.parseInt(uidArg); } catch (NumberFormatException e) { pw.println( "Invalid UID specified, can't convert to an integer - " + uidArg); return -1; } String packageName = getNextArgRequired(); String interfaceTypeArg = getNextArgRequired(); int interfaceType; switch (interfaceTypeArg) { case "STA": interfaceType = HDM_CREATE_IFACE_STA; break; case "AP": interfaceType = HDM_CREATE_IFACE_AP; break; case "AWARE": interfaceType = HDM_CREATE_IFACE_NAN; break; case "DIRECT": interfaceType = HDM_CREATE_IFACE_P2P; break; default: pw.println("Invalid interface type - expected STA|AP|AWARE|DIRECT: " + interfaceTypeArg); return -1; } boolean queryForNewInterface = false; String optArg = getNextArg(); if (optArg != null) { if (TextUtils.equals("-new", optArg)) { queryForNewInterface = true; } else { pw.println("Unknown extra arg --- " + optArg); return -1; } } List> details = mHalDeviceManager.reportImpactToCreateIface(interfaceType, queryForNewInterface, new WorkSource(uid, packageName)); final SparseArray ifaceMap = new SparseArray() {{ put(HDM_CREATE_IFACE_STA, "STA"); put(HDM_CREATE_IFACE_AP, "AP"); put(HDM_CREATE_IFACE_AP_BRIDGE, "AP"); put(HDM_CREATE_IFACE_P2P, "DIRECT"); put(HDM_CREATE_IFACE_NAN, "AWARE"); }}; if (details == null) { pw.println("Can't create interface: " + interfaceTypeArg); } else if (details.size() == 0) { pw.println("Interface " + interfaceTypeArg + " can be created without destroying any other interfaces"); } else { pw.println("Interface " + interfaceTypeArg + " can be created. Following interfaces will be destroyed:"); for (Pair detail : details) { pw.println(" Type=" + ifaceMap.get(detail.first) + ", WS=" + detail.second); } } return 0; } case "interface-priority-interactive-mode": { String flag = getNextArgRequired(); // enable|disable|default switch (flag) { case "enable": mInterfaceConflictManager.setUserApprovalNeededOverride(true, true); break; case "disable": mInterfaceConflictManager.setUserApprovalNeededOverride(true, false); break; case "default": mInterfaceConflictManager.setUserApprovalNeededOverride( false, /* don't care */ false); break; default: pw.println( "Invalid argument to `interface-priority-interactive-mode` - " + flag); return -1; } return 0; } case "set-one-shot-screen-on-delay-ms": { if (!SdkLevel.isAtLeastT()) { pw.println("This feature is only supported on SdkLevel T or later."); return -1; } int delay = Integer.parseInt(getNextArgRequired()); mWifiService.setOneShotScreenOnConnectivityScanDelayMillis(delay); return 0; } case "set-network-selection-config": { if (!SdkLevel.isAtLeastT()) { pw.println("This feature is only supported on SdkLevel T or later."); return -1; } WifiNetworkSelectionConfig.Builder builder = new WifiNetworkSelectionConfig.Builder(); builder.setSufficiencyCheckEnabledWhenScreenOff(getNextArgRequiredTrueOrFalse( "enabled", "disabled")); builder.setSufficiencyCheckEnabledWhenScreenOn(getNextArgRequiredTrueOrFalse( "enabled", "disabled")); String option = getNextOption(); while (option != null) { if (option.equals("-a")) { String associatedNetworkSelectionOverride = getNextArgRequired(); int override = Integer.parseInt(associatedNetworkSelectionOverride); builder.setAssociatedNetworkSelectionOverride(override); } else { pw.println("Ignoring unknown option " + option); } option = getNextOption(); } WifiNetworkSelectionConfig nsConfig; try { nsConfig = builder.build(); } catch (Exception e) { pw.println("Failed to build wifi network selection config."); return -1; } mWifiService.setNetworkSelectionConfig(nsConfig); return 0; } case "start-dpp-enrollee-responder": { CountDownLatch countDownLatch = new CountDownLatch(1); String option = getNextOption(); String info = null; int curve = 0; while (option != null) { if (option.equals("-i")) { info = getNextArgRequired(); } else if (option.equals("-c")) { curve = Integer.parseInt(getNextArgRequired()); } else { pw.println("Ignoring unknown option " + option); } option = getNextOption(); } mWifiService.startDppAsEnrolleeResponder(new Binder(), info, curve, new DppCallbackProxy(pw, countDownLatch)); // Wait for DPP callback countDownLatch.await(10000, TimeUnit.MILLISECONDS); return 0; } case "start-dpp-configurator-initiator": { CountDownLatch countDownLatch = new CountDownLatch(1); int netId = Integer.parseInt(getNextArgRequired()); int role = Integer.parseInt(getNextArgRequired()); String enrolleeUri = getNextArgRequired(); mWifiService.startDppAsConfiguratorInitiator(new Binder(), SHELL_PACKAGE_NAME, enrolleeUri, netId, role, new DppCallbackProxy(pw, countDownLatch)); // Wait for DPP callback countDownLatch.await(10000, TimeUnit.MILLISECONDS); return 0; } case "stop-dpp": mWifiService.stopDppSession(); return 0; case "set-ssid-charset": String lang = getNextArgRequired(); Charset charset = Charset.forName(getNextArgRequired()); mSsidTranslator.setMockLocaleCharset(lang, charset); return 0; case "clear-ssid-charsets": mSsidTranslator.clearMockLocaleCharsets(); return 0; case "take-bugreport": { if (mDeviceConfig.isInterfaceFailureBugreportEnabled()) { mWifiDiagnostics.takeBugReport("Wifi bugreport test", ""); } return 0; } case "get-allowed-channel": { StringBuilder allowedChannel = new StringBuilder(); int band = WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ; String option = getNextOption(); while (option != null) { if (option.equals("-b")) { band = Integer.parseInt(getNextArgRequired()); } else { pw.println("Ignoring unknown option: " + option); return -1; } option = getNextOption(); } try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } else { throw new UnsupportedOperationException(); } // The option "-b 2" (getting band 5ghz active channels) is valid for old // devices, but invalid for new devices. So we first check if device // supports getUsableChannels() API. mWifiService.getUsableChannels(WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ, WifiAvailableChannel.OP_MODE_STA, WifiAvailableChannel.FILTER_REGULATORY, SHELL_PACKAGE_NAME, extras); try { for (int opMode : OP_MODE_LIST) { List usableChannels = mWifiService.getUsableChannels(band, opMode, WifiAvailableChannel.FILTER_REGULATORY, SHELL_PACKAGE_NAME, extras); allowedChannel = new StringBuilder(); for (WifiAvailableChannel channel : usableChannels) { allowedChannel.append(channel.getFrequencyMhz()).append(" "); } pw.println("Allowed ch in " + getOpModeName(opMode) + " mode:\n" + allowedChannel); } } catch (IllegalArgumentException e) { pw.println("Invalid band: " + band); return -1; } } catch (UnsupportedOperationException e) { WifiScanner wifiScanner = mContext.getSystemService(WifiScanner.class); try { List availableChannels = wifiScanner.getAvailableChannels( band); for (Integer channel : availableChannels) { allowedChannel.append(channel).append(" "); } pw.println("Allowed ch in all modes:\n" + allowedChannel); } catch (SecurityException securityException) { pw.println("Permission is required."); return -1; } } catch (SecurityException e) { pw.println("Permission is required."); return -1; } return 0; } case "trigger-afc-location-update": Double longitude = Double.parseDouble(getNextArgRequired()); Double latitude = Double.parseDouble(getNextArgRequired()); Double height = Double.parseDouble(getNextArgRequired()); Location location = new Location(LocationManager.FUSED_PROVIDER); location.setLongitude(longitude); location.setLatitude(latitude); location.setAltitude(height); mWifiThreadRunner.post(() -> mAfcManager.onLocationChange(location, true), TAG + "#" + cmd); pw.println("The updated location with longitude of " + longitude + " degrees, " + "latitude of " + latitude + " degrees, and height of " + height + " meters was passed into the Afc Manager onLocationChange method."); return 0; case "set-afc-channel-allowance": { WifiChip.AfcChannelAllowance afcChannelAllowance = new WifiChip.AfcChannelAllowance(); boolean expiryTimeArgumentIncluded = false; boolean frequencyOrChannelArgumentIncluded = false; afcChannelAllowance.availableAfcFrequencyInfos = new ArrayList<>(); afcChannelAllowance.availableAfcChannelInfos = new ArrayList<>(); String option; while ((option = getNextOption()) != null) { switch (option) { case "-e": { int secondsUntilExpiry = Integer.parseInt(getNextArgRequired()); // AfcChannelAllowance requires this field to be a UNIX timestamp // in milliseconds. afcChannelAllowance.availabilityExpireTimeMs = System.currentTimeMillis() + secondsUntilExpiry * 1000; expiryTimeArgumentIncluded = true; break; } case "-f": { frequencyOrChannelArgumentIncluded = true; String frequenciesInput = getNextArgRequired(); if (frequenciesInput.equals(("none"))) { break; } // parse frequency list, and add it to the AfcChannelAllowance String[] unparsedFrequencies = frequenciesInput.split(":"); afcChannelAllowance.availableAfcFrequencyInfos = new ArrayList<>(); for (int i = 0; i < unparsedFrequencies.length; ++i) { String[] frequencyPieces = unparsedFrequencies[i].split(","); if (frequencyPieces.length != 3) { throw new IllegalArgumentException("Each frequency in the " + "available frequency list should have 3 values, " + "but found one with " + frequencyPieces.length); } WifiChip.AvailableAfcFrequencyInfo frequencyInfo = new WifiChip.AvailableAfcFrequencyInfo(); frequencyInfo.startFrequencyMhz = Integer.parseInt(frequencyPieces[0]); frequencyInfo.endFrequencyMhz = Integer.parseInt(frequencyPieces[1]); frequencyInfo.maxPsdDbmPerMhz = Integer.parseInt(frequencyPieces[2]); afcChannelAllowance.availableAfcFrequencyInfos .add(frequencyInfo); } break; } case "-c": { frequencyOrChannelArgumentIncluded = true; String channelsInput = getNextArgRequired(); if (channelsInput.equals("none")) { break; } // parse channel list, and add it to the AfcChannelAllowance String[] unparsedChannels = channelsInput.split(":"); afcChannelAllowance.availableAfcChannelInfos = new ArrayList<>(); for (int i = 0; i < unparsedChannels.length; ++i) { String[] channelPieces = unparsedChannels[i].split(","); if (channelPieces.length != 3) { throw new IllegalArgumentException("Each channel in the " + "available channel list should have 3 values, " + "but found one with " + channelPieces.length); } WifiChip.AvailableAfcChannelInfo channelInfo = new WifiChip.AvailableAfcChannelInfo(); channelInfo.globalOperatingClass = Integer.parseInt(channelPieces[0]); channelInfo.channelCfi = Integer.parseInt(channelPieces[1]); channelInfo.maxEirpDbm = Integer.parseInt(channelPieces[2]); afcChannelAllowance.availableAfcChannelInfos.add(channelInfo); } break; } default: { pw.println("Unrecognized command line argument."); return -1; } } } if (!expiryTimeArgumentIncluded) { pw.println("Please include the -e flag to set the seconds until the " + "availability expires."); return -1; } if (!frequencyOrChannelArgumentIncluded) { pw.println("Please include at least one of the -f or -c flags to set the " + "frequency or channel availability."); return -1; } ArrayBlockingQueue queue = new ArrayBlockingQueue<>(1); mWifiThreadRunner.post(() -> { if (mWifiNative.setAfcChannelAllowance(afcChannelAllowance)) { queue.offer("Successfully set the allowed AFC channels and " + "frequencies."); } else { queue.offer("Setting the allowed AFC channels and frequencies " + "failed."); } }, TAG + "#" + cmd); // block until msg is received, or timed out String msg = queue.poll(3000, TimeUnit.MILLISECONDS); if (msg == null) { pw.println("Setting the allowed AFC channels and frequencies timed out."); } else { pw.println(msg); } return 0; } case "get-cached-scan-data": WifiScanner.ScanData scanData = mWifiNative.getCachedScanResultsFromAllClientIfaces(); if (scanData.getResults().length > 0) { pw.println("Successfully get cached scan data: "); for (ScanResult scanResult : scanData.getResults()) { pw.println(scanResult); } } else { pw.println("Cached scan data is empty"); } return 0; case "configure-afc-server": final String url = getNextArgRequired(); if (!url.startsWith("http")) { pw.println("The required URL first argument is not a valid server URL for" + " a HTTP request."); return -1; } String secondOption = getNextOption(); Map requestProperties = new HashMap<>(); if (secondOption != null && secondOption.equals("-r")) { String key = getNextArg(); while (key != null) { String value = getNextArg(); // Check if there is a next value if (value != null) { requestProperties.put(key, value); } else { // Fail to proceed as there is no value given for the corresponding // key pw.println( "No value provided for the corresponding key " + key + ". There must be an even number of request" + " property arguments provided after the -r " + "option."); return -1; } key = getNextArg(); } } else { pw.println("No -r option was provided as second argument so the HTTP " + "request will have no request properties."); } mWifiThreadRunner.post(() -> { mAfcManager.setServerUrlAndRequestPropertyPairs(url, requestProperties); }, TAG + "#" + cmd); pw.println("The URL is set to " + url); pw.println("The request properties are set to: "); for (Map.Entry requestProperty : requestProperties.entrySet()) { pw.println("Key: " + requestProperty.getKey() + ", Value: " + requestProperty.getValue()); } return 0; case "set-mock-wifimodem-service": String opt = null; String serviceName = null; while ((opt = getNextOption()) != null) { switch (opt) { case "-s": { serviceName = getNextArgRequired(); break; } default: pw.println("set-mock-wifimodem-service requires '-s' option"); return -1; } } mWifiService.setMockWifiService(serviceName); // The result will be checked, must print result "true" pw.print("true"); return 0; case "get-mock-wifimodem-service": pw.print(mWifiNative.getMockWifiServiceName()); return 0; case "set-mock-wifimodem-methods": String methods = getNextArgRequired(); if (mWifiService.setMockWifiMethods(methods)) { pw.print("true"); } else { pw.print("fail to set mock method: " + methods); return -1; } return 0; case "force-overlay-config-value": int uid = Binder.getCallingUid(); if (!mWifiInjector.getWifiPermissionsUtil() .checkNetworkSettingsPermission(Binder.getCallingUid())) { pw.println("current shell caller Uid " + uid + " Missing NETWORK_SETTINGS permission"); return -1; } WifiResourceCache resourceCache = mContext.getResourceCache(); String type = getNextArgRequired(); String overlayName = getNextArgRequired(); boolean isEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled"); switch (type) { case "bool" -> { boolean value = false; if (isEnabled) { value = getNextArgRequiredTrueOrFalse("true", "false"); resourceCache.overrideBooleanValue(overlayName, value); } else { resourceCache.restoreBooleanValue(overlayName); } } case "integer" -> { int value = 0; if (isEnabled) { value = Integer.parseInt(getNextArgRequired()); resourceCache.overrideIntegerValue(overlayName, value); } else { resourceCache.restoreIntegerValue(overlayName); } } default -> { pw.print("require a valid type of the overlay"); return -1; } } pw.println("true"); return 0; case "get-overlay-config-values": mContext.getResourceCache().dump(pw); 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(); // Wrap the SSID in double quotes for UTF-8. The quotes may be removed if the SSID is in // hexadecimal digits, specified by the [-x] option below. 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 if (TextUtils.equals(type, "dpp")) { configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_DPP); } else if (TextUtils.equals(type, "wep")) { configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP); String password = getNextArgRequired(); // WEP-40, WEP-104, and WEP-256 if ((password.length() == 10 || password.length() == 26 || password.length() == 58) && password.matches("[0-9A-Fa-f]*")) { configuration.wepKeys[0] = password; } else { configuration.wepKeys[0] = '"' + password + '"'; } } else { throw new IllegalArgumentException("Unknown network type " + type); } String option = getNextOption(); while (option != null) { if (option.equals("-x")) { configuration.SSID = ssid; } else 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 ssidStr = getNextArgRequired(); String type = getNextArgRequired(); SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); configBuilder.setSsid(ssidStr); 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 if (TextUtils.equals(type, "owe_transition")) { configBuilder.setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION); } else if (TextUtils.equals(type, "owe")) { configBuilder.setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_WPA3_OWE); } 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.startsWith("bridged")) { if (!SdkLevel.isAtLeastS()) { throw new IllegalArgumentException( "-b bridged* option is not supported before S"); } switch (preferredBand) { case "bridged": // fall through case "bridged_2_5": configBuilder.setBands(new int[] { SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ}); break; case "bridged_2_6": configBuilder.setBands(new int[] { SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_6GHZ}); break; case "bridged_5_6": configBuilder.setBands(new int[] { SoftApConfiguration.BAND_5GHZ, SoftApConfiguration.BAND_6GHZ}); break; default: throw new IllegalArgumentException("Invalid bridged band option " + preferredBand); } } else { throw new IllegalArgumentException("Invalid band option " + preferredBand); } } else if (SdkLevel.isAtLeastT() && option.equals("-x")) { configBuilder.setWifiSsid(WifiSsid.fromString(ssidStr)); } else if (option.equals("-f")) { SparseIntArray channels = new SparseIntArray(); while (getRemainingArgsCount() > 0) { int apChannelMHz; try { apChannelMHz = Integer.parseInt(getNextArgRequired()); } catch (NumberFormatException e) { throw new IllegalArgumentException( "-f option requires a valid channel frequency"); } channels.put(ApConfigUtil.convertFrequencyToBand(apChannelMHz), ScanResult.convertFrequencyMhzToChannelIfSupported(apChannelMHz)); } if (channels.size() == 0) { throw new IllegalArgumentException( "-f option requires a valid channel frequency atleast"); } if (SdkLevel.isAtLeastS()) { configBuilder.setChannels(channels); } else { if (channels.size() > 1) { throw new IllegalArgumentException( "dual channels are not supported before S"); } configBuilder.setChannel(channels.valueAt(0), channels.keyAt(0)); } } else if (option.equals("-w")) { String bandwidth = getNextArgRequired(); if (bandwidth.equals("20")) { configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ); } else if (bandwidth.equals("40")) { configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_40MHZ); } else if (bandwidth.equals("80")) { configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_80MHZ); } else if (bandwidth.equals("160")) { configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_160MHZ); } else if (bandwidth.equals("320")) { configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_320MHZ); } else { throw new IllegalArgumentException("Invalid bandwidth option " + bandwidth); } } 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 Pair buildNetworkRequest(PrintWriter pw) { String firstOpt = getNextOption(); boolean isGlob = "-g".equals(firstOpt); boolean noSsid = "-s".equals(firstOpt); String ssid = noSsid ? "NoSsid" : getNextArgRequired(); String type = noSsid ? null : getNextArgRequired(); WifiNetworkSpecifier.Builder specifierBuilder = new WifiNetworkSpecifier.Builder(); if (isGlob) { specifierBuilder.setSsidPattern( new PatternMatcher(ssid, PatternMatcher.PATTERN_ADVANCED_GLOB)); } else { if (ssid != null && !noSsid) specifierBuilder.setSsid(ssid); } if (type != null) { 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(); String ssidKey = ssid; boolean nullBssid = false; boolean hasInternet = false; while (option != null) { if (option.equals("-b")) { bssid = getNextArgRequired(); } else if (option.equals("-n")) { nullBssid = true; } else if (option.equals("-d")) { String band = getNextArgRequired(); ssidKey = ssidKey + "_" + band + "g"; if (band.equals("2")) { specifierBuilder.setBand(ScanResult.WIFI_BAND_24_GHZ); } else if (band.equals("5")) { specifierBuilder.setBand(ScanResult.WIFI_BAND_5_GHZ); } else if (band.equals("6")) { specifierBuilder.setBand(ScanResult.WIFI_BAND_6_GHZ); } else if (band.equals("60")) { specifierBuilder.setBand(ScanResult.WIFI_BAND_60_GHZ); } else { throw new IllegalArgumentException("Unknown band " + band); } } else if (option.equals("-i")) { ssidKey = ssidKey + "_internet"; hasInternet = true; } else { pw.println("Ignoring unknown option " + option); } option = getNextOption(); } if (bssid != null && nullBssid) { throw new IllegalArgumentException("Invalid option combination: " + "Should not use both -b and -n at the same time."); } // 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 && !nullBssid && !noSsid) { ScanResult matchingScanResult = mScanRequestProxy.getScanResults() .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 && !nullBssid) specifierBuilder.setBssid(MacAddress.fromString(bssid)); NetworkRequest.Builder builder = new NetworkRequest.Builder() .addTransportType(TRANSPORT_WIFI); if (hasInternet) { builder.addCapability(NET_CAPABILITY_INTERNET); } else { builder.removeCapability(NET_CAPABILITY_INTERNET); } return new Pair(ssidKey, builder.setNetworkSpecifier(specifierBuilder.build()).build()); } @RequiresApi(Build.VERSION_CODES.S) @NonNull private List buildCoexCellChannels() { List 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 int sendLinkProbe(PrintWriter pw) throws InterruptedException { // Note: should match WifiNl80211Manager#SEND_MGMT_FRAME_TIMEOUT_MS final int sendMgmtFrameTimeoutMs = 1000; ArrayBlockingQueue 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), TAG + "#sendLinkProbe"); // 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.getConnectionInfo(); printWifiInfo(pw, info); if (info.getSupplicantState() != SupplicantState.COMPLETED) { continue; } Network network = cm.getCurrentNetwork(); 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(" connect-network open|owe|wpa2|wpa3|wep [] [-x] [-m] " + "[-d] [-b ] [-r auto|none|persistent|non_persistent]"); pw.println(" Connect to a network with provided params and add to saved networks list"); pw.println(" - SSID of the network"); pw.println(" open|owe|wpa2|wpa3|wep - 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' or 'wep' for networks with passphrase"); pw.println(" - 'wpa2' - WPA-2 PSK networks (Most prevalent)"); pw.println(" - 'wpa3' - WPA-3 PSK networks"); pw.println(" - 'wep' - WEP network. Passphrase should be bytes in hex or encoded" + " into String using UTF-8"); pw.println(" -x - Specifies the SSID as hex digits instead of plain text"); 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 - Set specific BSSID."); pw.println(" -r auto|none|persistent|non_persistent - MAC randomization scheme for the" + " network"); pw.println(" add-network open|owe|wpa2|wpa3|wep [] [-x] [-m] [-d] " + "[-b ] [-r auto|none|persistent|non_persistent]"); pw.println(" Add/update saved network with provided params"); pw.println(" - SSID of the network"); pw.println(" open|owe|wpa2|wpa3|wep - 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(" - 'wep' - WEP network. Passphrase should be bytes in hex or encoded" + " into String using UTF-8"); pw.println(" -x - Specifies the SSID as hex digits instead of plain text"); 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 - Set specific BSSID."); pw.println(" -r auto|none|persistent|non_persistent - MAC randomization scheme for the" + " network"); 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(" forget-network "); pw.println(" Remove the network mentioned by "); pw.println(" - Use list-networks to retrieve for the network"); pw.println(" status"); pw.println(" Current wifi status"); pw.println(" set-verbose-logging enabled|disabled [-l ]"); pw.println(" Set the verbose logging enabled or disabled with log level"); pw.println(" -l - verbose logging level"); pw.println(" 0 - verbose logging disabled"); pw.println(" 1 - verbose logging enabled"); pw.println(" 2 - verbose logging Show key mode"); pw.println(" 3 - verbose logging only for Wi-Fi Aware feature"); 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 open|owe|wpa2|wpa3 [] [-u] [-o] [-p] [-m] " + " [-s] [-d] [-b ] [-e] [-i] [-a ] [-c ]"); 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 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 - Set specific BSSID."); pw.println(" -r - Enable non_persistent randomization (disabled by default)"); pw.println(" -a - Mark the suggestion carrier merged"); pw.println(" -c - set carrier Id"); pw.println(" -i - set subscription Id, if -a is used, " + "this must be set"); pw.println(" remove-suggestion [-l]"); pw.println(" Remove a network suggestion with provided SSID of the network"); pw.println(" -l - Remove suggestion with lingering, if not set will disconnect " + "immediately "); 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 " + " " + " " + " " + "] ..."); 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 "); 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(" - 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(" pmksa-flush "); pw.println(" - Flush the local PMKSA cache associated with the network id." + " Use list-networks to retrieve for the network"); pw.println(" reload-resources"); pw.println( " Reset the WiFi resources cache which will cause them to be reloaded next " + "time they are accessed. Necessary if overlays are manually modified."); pw.println(" launch-dialog-simple [-t ] [-m <message>]" + " [-l <url> <url_start> <url_end>] [-y <positive_button_text>]" + " [-n <negative_button_text>] [-x <neutral_button_text>] [-c <timeout_millis>]"); pw.println(" Launches a simple dialog and waits up to 15 seconds to" + " print the response."); pw.println(" -t - Title"); pw.println(" -m - Message"); pw.println(" -l - URL of the message, with the start and end index inside the message"); pw.println(" -y - Positive Button Text"); pw.println(" -n - Negative Button Text"); pw.println(" -x - Neutral Button Text"); pw.println(" -c - Optional timeout in milliseconds"); pw.println(" -s - Use the legacy dialog implementation on the system process"); pw.println(" launch-dialog-p2p-invitation-sent <device_name> <pin> [-i <display_id>]"); pw.println(" Launches a P2P Invitation Sent dialog."); pw.println(" <device_name> - Name of the device the invitation was sent to"); pw.println(" <pin> - PIN for the invited device to input"); pw.println(" launch-dialog-p2p-invitation-received <device_name> [-p] [-d <pin>] " + "[-i <display_id>] [-c <timeout_millis>]"); pw.println(" Launches a P2P Invitation Received dialog and waits up to 15 seconds to" + " print the response."); pw.println(" <device_name> - Name of the device sending the invitation"); pw.println(" -p - Show PIN input"); pw.println(" -d - Display PIN <pin>"); pw.println(" -i - Display ID"); pw.println(" -c - Optional timeout in milliseconds"); pw.println(" query-interface <uid> <package_name> STA|AP|AWARE|DIRECT [-new]"); pw.println( " Query whether the specified could be created for the specified UID and " + "package name, and if so - what other interfaces would be destroyed"); pw.println(" -new - query for a new interfaces (otherwise an existing interface is ok"); pw.println(" interface-priority-interactive-mode enable|disable|default"); pw.println(" Enable or disable asking the user when there's an interface priority " + "conflict, |default| implies using the device default behavior."); pw.println(" set-one-shot-screen-on-delay-ms <delayMs>"); pw.println(" set the delay for the next screen-on connectivity scan in milliseconds."); pw.println(" set-network-selection-config <enabled|disabled> <enabled|disabled> " + "-a <associated_network_selection_override>"); pw.println(" set whether sufficiency check is enabled for screen off case " + "(first arg), and screen on case (second arg)"); pw.println(" -a - set as one of the int " + "WifiNetworkSelectionConfig.ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ values:"); pw.println(" 0 - no override"); pw.println(" 1 - override to enabled"); pw.println(" 2 - override to disabled"); 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(" take-bugreport"); pw.println( " take bugreport through betterBug. " + "If it failed, take bugreport through bugreport manager."); pw.println(" get-allowed-channel [-b 1|6|7|8|15|16|31]"); pw.println( " get allowed channels in each operation mode from wifiManager if available. " + "Otherwise, it returns from wifiScanner."); pw.println(" -b - set the band in which channels are allowed"); pw.println(" '1' - band 2.4 GHz"); pw.println(" '6' - band 5 GHz with DFS channels"); pw.println(" '7' - band 2.4 and 5 GHz with DFS channels"); pw.println(" '8' - band 6 GHz"); pw.println(" '15' - band 2.4, 5, and 6 GHz with DFS channels"); pw.println(" '16' - band 60 GHz"); pw.println(" '31' - band 2.4, 5, 6 and 60 GHz with DFS channels"); pw.println(" get-cached-scan-data"); pw.println(" Gets scan data cached by the firmware"); pw.println(" force-overlay-config-value bool|integer <overlayName> enabled|disabled" + "<configValue>"); pw.println(" Force overlay to a specified value."); pw.println(" bool|integer - specified the type of the overlay"); pw.println(" <overlayName> - name of the overlay whose value is overridden."); pw.println(" enabled|disabled: enable the override or disable it and revert to using " + "the built-in value."); pw.println(" <configValue> - override value of the overlay." + "Must match the overlay type"); pw.println(" get-overlay-config-values"); pw.println(" Get current overlay value in resource cache."); 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."); } private void onHelpPrivileged(PrintWriter pw) { 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( " start-softap <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition)" + " <passphrase> [-b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6]" + " [-x] [-w 20|40|80|160|320] [-f <int> [<int>]]"); 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|owe|owe_transition - Security type of the " + "network."); pw.println(" - Use 'open', 'owe', 'owe_transition' for networks with no passphrase"); pw.println(" - Use 'wpa2', 'wpa3', 'wpa3_transition' for networks with passphrase"); pw.println(" -b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6 - select the" + " preferred bands."); 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(" - Use 'bridged_2_5' to indicate bridged AP which enables APs on both " + "2.4G + 5G"); pw.println(" - Use 'bridged_2_6' to indicate bridged AP which enables APs on both " + "2.4G + 6G"); pw.println(" - Use 'bridged_5_6' to indicate bridged AP which enables APs on both " + "5G + 6G"); 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 or '-f <int> <int>' option"); pw.println(" -f <int> <int> - force exact channel frequency for operation channel"); pw.println(" Note: -f <int> <int> - must be the last option"); pw.println(" For example:"); pw.println(" Use '-f 2412' to enable single Soft Ap on 2412"); pw.println(" Use '-f 2412 5745' to enable bridged dual Soft Ap on 2412 and 5745"); pw.println(" -x - Specifies the SSID as hex digits instead of plain text (T and above)"); pw.println(" -w 20|40|80|160|320 - select the maximum channel bandwidth (MHz)"); pw.println(" stop-softap"); pw.println(" Stop softap (hotspot)"); 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 [-w <maxBandwidth>]"); pw.println(" Sets whether soft AP channel is forced to <int> MHz [-w <maxBandwidth>]"); pw.println(" -w 0|20|40|80|160|320 - select the maximum channel bandwidth (MHz)"); pw.println(" Note: If the bandwidth option is not provided or set to 0, framework" + " will set the maximum bandwidth to auto, allowing HAL to select the bandwidth"); 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(" or '00' for forcing to world mode country code"); 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(" settings-reset"); pw.println(" Initiates wifi settings reset"); pw.println(" allow-root-to-get-local-only-cmm enabled|disabled"); pw.println(" sets whether the shell running as root could use the local-only secondary " + "STA"); pw.println(" add-request [-g] [-i] [-n] [-s] <ssid> open|owe|wpa2|wpa3 [<passphrase>]" + " [-b <bssid>] [-d <band=2|5|6|60>]"); 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(" -g - Marks the following SSID as a glob pattern"); pw.println(" <ssid> - SSID of the network, or glob pattern if -g is present"); 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(" -i Set internet capability."); pw.println(" -d Specify the band of access point: 2, 5, 6, or 60"); pw.println(" -s No SSID provided, to be chosen by network selection."); pw.println(" -n - Prevent auto-selection of BSSID and force it to be null so that the " + "request matches all BSSIDs."); 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(" set-emergency-scan-request enabled|disabled"); pw.println(" Sets whether there is a emergency scan request in progress."); 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 [-x] <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(" -x - Specifies the SSID as hex digits instead of plain text"); 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."); pw.println(" set-passpoint-enabled enabled|disabled"); pw.println(" Sets whether Passpoint should be enabled or disabled"); pw.println( " start-lohs <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition)" + " <passphrase> [-b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6]" + " [-x] [-w 20|40|80|160|320] [-f <int> [<int>]])"); pw.println(" Start local only softap (hotspot) with provided params"); pw.println(" <ssid> - SSID of the network"); pw.println(" open|wpa2|wpa3|wpa3_transition|owe|owe_transition - Security type of the " + "network."); pw.println(" - Use 'open', 'owe', 'owe_transition' for networks with no passphrase"); pw.println(" - Use 'wpa2', 'wpa3', 'wpa3_transition' for networks with passphrase"); pw.println(" -b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6 - select the " + "preferred bands."); 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(" - Use 'bridged_2_5' to indicate bridged AP which enables APs on both " + "2.4G + 5G"); pw.println(" - Use 'bridged_2_6' to indicate bridged AP which enables APs on both " + "2.4G + 6G"); pw.println(" - Use 'bridged_5_6' to indicate bridged AP which enables APs on both " + "5G + 6G"); 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 or '-f <int> <int>' option"); pw.println(" -f <int> <int> - force exact channel frequency for operation channel"); pw.println(" Note: -f <int> <int> - must be the last option"); pw.println(" For example:"); pw.println(" Use '-f 2412' to enable single Soft Ap on 2412"); pw.println(" Use '-f 2412 5745' to enable bridged dual lohs on 2412 and 5745"); pw.println(" -x - Specifies the SSID as hex digits instead of plain text (T and above)"); pw.println(" -w 20|40|80|160|320 - select the maximum bandwidth (MHz)"); pw.println(" stop-softap"); pw.println(" Stop softap (hotspot)"); pw.println(" Note: If the band option is not provided, 2.4GHz is the preferred band."); pw.println(" stop-lohs"); pw.println(" Stop local only softap (hotspot)"); pw.println(" set-multi-internet-mode 0|1|2"); pw.println(" Sets Multi Internet use case mode. 0-disabled 1-dbs 2-multi ap"); pw.println(" set-pno-request <ssid> [-f <frequency>]"); pw.println(" Requests to include a non-quoted UTF-8 SSID in PNO scans"); pw.println(" clear-pno-request"); pw.println(" Clear the PNO scan request."); pw.println(" start-dpp-enrollee-responder [-i <info>] [-c <curve>]"); pw.println(" Start DPP Enrollee responder mode."); pw.println(" -i - Device Info to be used in DPP Bootstrapping URI"); pw.println(" -c - Cryptography Curve integer 1:p256v1, 2:s384r1, etc"); pw.println(" start-dpp-configurator-initiator <networkId> <netRole> <enrolleeURI>"); pw.println(" Start DPP Configurator Initiator mode."); pw.println(" netRole - 0: STA, 1: AP"); pw.println(" enrolleeURI - Bootstrapping URI received from Enrollee"); pw.println(" stop-dpp"); pw.println(" Stop DPP session."); pw.println(" set-ssid-charset <locale_language> <charset_name>"); pw.println(" Sets the SSID translation charset for the given locale language."); pw.println(" Example: set-ssid-charset zh GBK"); pw.println(" clear-ssid-charsets"); pw.println(" Clears the SSID translation charsets set in set-ssid-charset."); pw.println(" get-last-caller-info api_type"); pw.println(" Get the last caller information for a WifiManager.ApiType"); pw.println(" trigger-afc-location-update <longitude> <latitude> <height>"); pw.println(" Passes in longitude, latitude, and height values as arguments of type " + "double for a fake location update to trigger framework logic to query the AFC " + "server. The longitude and latitude pair is in decimal degrees and the height" + " is the altitude in meters. The server URL needs to have been previously set " + "with the configure-afc-server shell command."); pw.println(" Example: trigger-afc-location-update 37.425056 -122.984157 3.043"); pw.println(" set-afc-channel-allowance -e <secs_until_expiry> [-f <low_freq>,<high_freq>," + "<psd>:...|none] [-c <operating_class>,<channel_cfi>,<max_eirp>:...|none]"); pw.println(" Sets the allowed AFC channels and frequencies."); pw.println(" -e - Seconds until the availability expires."); pw.println(" -f - Colon-separated list of available frequency info."); pw.println(" Note: each frequency should contain 3 comma separated values, where " + "the first is the low frequency (MHz), the second the high frequency (MHz), the " + "third the max PSD (dBm per MHz). To set an empty frequency list, enter \"none\" " + "in place of the list of allowed frequencies."); pw.println(" -c - Colon-separated list of available channel info."); pw.println(" Note: each channel should contain 3 comma separated values, where " + "the first is the global operating class, the second the channel CFI, " + "the third the max EIRP in dBm. To set an empty channel list, enter \"none\" in " + "place of the list of allowed channels."); pw.println(" Example: set-afc-channel-allowance -e 30 -c none -f " + "5925,6020,23:6020,6050,1"); pw.println(" configure-afc-server <url> [-r [<request property key> <request property " + "value>] ...]"); pw.println(" Sets the server URL and request properties for the HTTP request which the " + "AFC Client will query."); pw.println(" -r - HTTP header fields that come in pairs of key and value which are added" + " to the HTTP request. Must be an even number of arguments. If there is no -r " + "option provided or no arguments provided after the -r option, then set the " + "request properties to none in the request."); pw.println(" Example: configure-afc-server https://testURL -r key1 value1 key2 value2"); } @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("/")))); } } } }