blob: 367979f3c07d0b9dd91e06d80f6fe1b95b46d276 [file] [log] [blame]
/*
* 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.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.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.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.SupplicantState;
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.WifiNetworkSpecifier;
import android.net.wifi.WifiNetworkSuggestion;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiSsid;
import android.net.wifi.util.ScanResultUtil;
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.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.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.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Interprets and executes 'adb shell cmd wifi [args]'.
*
* To add new commands:
* - onCommand: Add a case "<command>" execute. Return a 0
* if command executed successfully.
* - onHelp: add a description string.
*
* Permissions: currently root permission is required for some commands. Others will
* enforce the corresponding API permissions.
*/
public class WifiShellCommand extends BasicShellCommandHandler {
@VisibleForTesting
public static String SHELL_PACKAGE_NAME = "com.android.shell";
// These don't require root access.
// However, these do perform permission checks in the corresponding WifiService methods.
private static final String[] NON_PRIVILEGED_COMMANDS = {
"add-suggestion",
"forget-network",
"get-country-code",
"help",
"-h",
"is-verbose-logging",
"list-scan-results",
"list-networks",
"list-suggestions",
"remove-suggestion",
"remove-all-suggestions",
"reset-connected-score",
"set-connected-score",
"set-scan-always-available",
"set-verbose-logging",
"set-wifi-enabled",
"set-passpoint-enabled",
"set-multi-internet-state",
"start-scan",
"start-softap",
"status",
"stop-softap",
"query-interface",
"interface-priority-interactive-mode",
"set-one-shot-screen-on-delay-ms",
"set-ipreach-disconnect",
"get-ipreach-disconnect",
};
private static final Map<String, Pair<NetworkRequest, ConnectivityManager.NetworkCallback>>
sActiveRequests = new ConcurrentHashMap<>();
private final ActiveModeWarden mActiveModeWarden;
private final WifiGlobals mWifiGlobals;
private final WifiLockManager mWifiLockManager;
private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
private final WifiConfigManager mWifiConfigManager;
private final WifiNative mWifiNative;
private final CoexManager mCoexManager;
private final WifiCountryCode mWifiCountryCode;
private final WifiLastResortWatchdog mWifiLastResortWatchdog;
private final WifiServiceImpl mWifiService;
private final 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 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(int state, int failureReason) {
mPrintWriter.println("onStateChanged with state: " + state
+ " failure reason: " + failureReason);
mSapState = state;
if (state == 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 (state == WifiManager.WIFI_AP_STATE_DISABLED) {
mPrintWriter.println(" SAP is disabled");
} else if (state == WifiManager.WIFI_AP_STATE_FAILED) {
mPrintWriter.println(" SAP failed to start");
mCountDownLatch.countDown();
}
}
@Override
public void onConnectedClientsOrInfoChanged(Map<String, SoftApInfo> infos,
Map<String, List<WifiClient>> 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();
}
public Integer getSessionId() {
return mSessionId;
}
public IScoreUpdateObserver getScoreUpdateObserver() {
return mScoreUpdateObserver;
}
}
WifiShellCommand(WifiInjector wifiInjector, WifiServiceImpl wifiService, WifiContext context,
WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner) {
mWifiGlobals = wifiGlobals;
mWifiThreadRunner = wifiThreadRunner;
mActiveModeWarden = wifiInjector.getActiveModeWarden();
mWifiLockManager = wifiInjector.getWifiLockManager();
mWifiNetworkSuggestionsManager = wifiInjector.getWifiNetworkSuggestionsManager();
mWifiConfigManager = wifiInjector.getWifiConfigManager();
mWifiNative = wifiInjector.getWifiNative();
mCoexManager = wifiInjector.getCoexManager();
mWifiCountryCode = wifiInjector.getWifiCountryCode();
mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
mWifiService = wifiService;
mContext = context;
mConnectivityManager = context.getSystemService(ConnectivityManager.class);
mWifiCarrierInfoManager = wifiInjector.getWifiCarrierInfoManager();
mWifiNetworkFactory = wifiInjector.getWifiNetworkFactory();
mSelfRecovery = wifiInjector.getSelfRecovery();
mWifiApConfigStore = wifiInjector.getWifiApConfigStore();
mScanRequestProxy = wifiInjector.getScanRequestProxy();
mWifiDialogManager = wifiInjector.getWifiDialogManager();
mHalDeviceManager = wifiInjector.getHalDeviceManager();
mInterfaceConflictManager = wifiInjector.getInterfaceConflictManager();
}
@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");
mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(approved,
Binder.getCallingUid(), packageName);
return 0;
}
case "network-suggestions-has-user-approved": {
String packageName = getNextArgRequired();
boolean hasUserApproved =
mWifiNetworkSuggestionsManager.hasUserApprovedForApp(packageName);
pw.println(hasUserApproved ? "yes" : "no");
return 0;
}
case "imsi-protection-exemption-set-user-approved-for-carrier": {
String arg1 = getNextArgRequired();
int carrierId = -1;
try {
carrierId = Integer.parseInt(arg1);
} catch (NumberFormatException e) {
pw.println("Invalid argument to "
+ "'imsi-protection-exemption-set-user-approved-for-carrier' "
+ "- carrierId must be an Integer");
return -1;
}
boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
mWifiCarrierInfoManager
.setHasUserApprovedImsiPrivacyExemptionForCarrier(approved, carrierId);
return 0;
}
case "imsi-protection-exemption-has-user-approved-for-carrier": {
String arg1 = getNextArgRequired();
int carrierId = -1;
try {
carrierId = Integer.parseInt(arg1);
} catch (NumberFormatException e) {
pw.println("Invalid argument to "
+ "'imsi-protection-exemption-has-user-approved-for-carrier' "
+ "- 'carrierId' must be an Integer");
return -1;
}
boolean hasUserApproved = mWifiCarrierInfoManager
.hasUserApprovedImsiPrivacyExemptionForCarrier(carrierId);
pw.println(hasUserApproved ? "yes" : "no");
return 0;
}
case "imsi-protection-exemption-clear-user-approved-for-carrier": {
String arg1 = getNextArgRequired();
int carrierId = -1;
try {
carrierId = Integer.parseInt(arg1);
} catch (NumberFormatException e) {
pw.println("Invalid argument to "
+ "'imsi-protection-exemption-clear-user-approved-for-carrier' "
+ "- 'carrierId' must be an Integer");
return -1;
}
mWifiCarrierInfoManager.clearImsiPrivacyExemptionForCarrier(carrierId);
return 0;
}
case "network-requests-remove-user-approved-access-points": {
String packageName = getNextArgRequired();
mWifiNetworkFactory.removeUserApprovedAccessPointsForApp(packageName);
return 0;
}
case "clear-user-disabled-networks": {
mWifiConfigManager.clearUserTemporarilyDisabledList();
return 0;
}
case "send-link-probe": {
return sendLinkProbe(pw);
}
case "force-softap-band": {
boolean forceBandEnabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
if (forceBandEnabled) {
String forcedBand = getNextArgRequired();
if (forcedBand.equals("2")) {
mWifiApConfigStore.enableForceSoftApBandOrChannel(
SoftApConfiguration.BAND_2GHZ, 0);
} else if (forcedBand.equals("5")) {
mWifiApConfigStore.enableForceSoftApBandOrChannel(
SoftApConfiguration.BAND_5GHZ, 0);
} else if (forcedBand.equals("6")) {
mWifiApConfigStore.enableForceSoftApBandOrChannel(
SoftApConfiguration.BAND_6GHZ, 0);
} else {
pw.println("Invalid argument to 'force-softap-band enabled' "
+ "- must be a valid band integer (2|5|6)");
return -1;
}
return 0;
} else {
mWifiApConfigStore.disableForceSoftApBandOrChannel();
return 0;
}
}
case "force-softap-channel": {
boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
if (enabled) {
int apChannelMHz;
try {
apChannelMHz = Integer.parseInt(getNextArgRequired());
} catch (NumberFormatException e) {
pw.println("Invalid argument to 'force-softap-channel enabled' "
+ "- must be a positive integer");
return -1;
}
int apChannel = ScanResult.convertFrequencyMhzToChannelIfSupported(
apChannelMHz);
int band = ApConfigUtil.convertFrequencyToBand(apChannelMHz);
pw.println("channel: " + apChannel + " band: " + band);
if (apChannel == -1 || band == -1) {
pw.println("Invalid argument to 'force-softap-channel enabled' "
+ "- must be a valid WLAN channel");
return -1;
}
boolean isTemporarilyEnablingWifiNeeded = mWifiService.getWifiEnabledState()
!= WIFI_STATE_ENABLED;
if (isTemporarilyEnablingWifiNeeded) {
waitForWifiEnabled(true);
}
// Following calls will fail if wifi is not enabled
boolean isValidChannel = isApChannelMHzValid(pw, apChannelMHz);
if (isTemporarilyEnablingWifiNeeded) {
waitForWifiEnabled(false);
}
if (!isValidChannel
|| (band == SoftApConfiguration.BAND_5GHZ
&& !mWifiService.is5GHzBandSupported())
|| (band == SoftApConfiguration.BAND_6GHZ
&& !mWifiService.is6GHzBandSupported())
|| (band == SoftApConfiguration.BAND_60GHZ
&& !mWifiService.is60GHzBandSupported())) {
pw.println("Invalid argument to 'force-softap-channel enabled' "
+ "- must be a valid WLAN channel"
+ " in a band supported by the device");
return -1;
}
mWifiApConfigStore.enableForceSoftApBandOrChannel(band, apChannel);
return 0;
} else {
mWifiApConfigStore.disableForceSoftApBandOrChannel();
return 0;
}
}
case "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<ScanResult> 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<ScanResult> scanResults =
mWifiService.getScanResults(SHELL_PACKAGE_NAME, null);
if (scanResults.isEmpty()) {
pw.println("No scan results");
} else {
ScanResultUtil.dumpScanResults(pw, scanResults,
SystemClock.elapsedRealtime());
}
return 0;
case "start-scan":
mWifiService.startScan(SHELL_PACKAGE_NAME, null);
return 0;
case "list-networks":
ParceledListSlice<WifiConfiguration> networks =
mWifiService.getConfiguredNetworks(SHELL_PACKAGE_NAME, null, false);
if (networks == null || networks.getList().isEmpty()) {
pw.println("No networks");
} else {
pw.println("Network Id SSID Security type");
for (WifiConfiguration network : networks.getList()) {
String securityType = network.getSecurityParamsList().stream()
.map(p -> WifiConfiguration.getSecurityTypeName(
p.getSecurityType())
+ (p.isAddedByAutoUpgrade() ? "^" : ""))
.collect(Collectors.joining("/"));
pw.println(String.format("%-12d %-32s %-4s",
network.networkId, WifiInfo.sanitizeSsid(network.SSID),
securityType));
}
}
return 0;
case "connect-network": {
CountDownLatch countDownLatch = new CountDownLatch(1);
IActionListener.Stub actionListener = new IActionListener.Stub() {
@Override
public void onSuccess() throws RemoteException {
pw.println("Connection initiated ");
countDownLatch.countDown();
}
@Override
public void onFailure(int i) throws RemoteException {
pw.println("Connection failed");
countDownLatch.countDown();
}
};
WifiConfiguration config = buildWifiConfiguration(pw);
mWifiService.connect(config, -1, actionListener, SHELL_PACKAGE_NAME);
// wait for status.
countDownLatch.await(500, TimeUnit.MILLISECONDS);
setAutoJoin(pw, config.SSID, config.allowAutojoin);
return 0;
}
case "add-network": {
CountDownLatch countDownLatch = new CountDownLatch(1);
IActionListener.Stub actionListener = new IActionListener.Stub() {
@Override
public void onSuccess() throws RemoteException {
pw.println("Save successful");
countDownLatch.countDown();
}
@Override
public void onFailure(int i) throws RemoteException {
pw.println("Save failed");
countDownLatch.countDown();
}
};
WifiConfiguration config = buildWifiConfiguration(pw);
mWifiService.save(config, actionListener, SHELL_PACKAGE_NAME);
// wait for status.
countDownLatch.await(500, TimeUnit.MILLISECONDS);
setAutoJoin(pw, config.SSID, config.allowAutojoin);
return 0;
}
case "forget-network": {
String networkId = getNextArgRequired();
CountDownLatch countDownLatch = new CountDownLatch(1);
IActionListener.Stub actionListener = new IActionListener.Stub() {
@Override
public void onSuccess() throws RemoteException {
pw.println("Forget successful");
countDownLatch.countDown();
}
@Override
public void onFailure(int i) throws RemoteException {
pw.println("Forget failed");
countDownLatch.countDown();
}
};
mWifiService.forget(Integer.parseInt(networkId), actionListener);
// wait for status.
countDownLatch.await(500, TimeUnit.MILLISECONDS);
return 0;
}
case "pmksa-flush": {
String networkId = getNextArgRequired();
int netId = Integer.parseInt(networkId);
WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(netId);
if (config == null) {
pw.println("No Wifi config corresponding to networkId: " + netId);
return -1;
}
mWifiNative.removeNetworkCachedData(netId);
return 0;
}
case "status":
printStatus(pw);
return 0;
case "set-verbose-logging": {
boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
mWifiService.enableVerboseLogging(enabled ? 1 : 0);
return 0;
}
case "is-verbose-logging": {
int enabled = mWifiService.getVerboseLoggingLevel();
pw.println(enabled > 0 ? "enabled" : "disabled");
return 0;
}
case "start-restricting-auto-join-to-subscription-id": {
if (!SdkLevel.isAtLeastS()) {
pw.println("This feature is only supported on SdkLevel S or later.");
return -1;
}
int subId = Integer.parseInt(getNextArgRequired());
mWifiService.startRestrictingAutoJoinToSubscriptionId(subId);
return 0;
}
case "stop-restricting-auto-join-to-subscription-id": {
if (!SdkLevel.isAtLeastS()) {
pw.println("This feature is only supported on SdkLevel S or later.");
return -1;
}
mWifiService.stopRestrictingAutoJoinToSubscriptionId();
return 0;
}
case "add-suggestion": {
WifiNetworkSuggestion suggestion = buildSuggestion(pw);
if (suggestion == null) {
pw.println("Invalid network suggestion parameter");
return -1;
}
int errorCode = mWifiService.addNetworkSuggestions(
Arrays.asList(suggestion), SHELL_PACKAGE_NAME, null);
if (errorCode != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
pw.println("Add network suggestion failed with error code: " + errorCode);
return -1;
}
// untrusted/oem-paid networks need a corresponding NetworkRequest.
if (suggestion.isUntrusted()
|| (SdkLevel.isAtLeastS()
&& (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
NetworkRequest.Builder networkRequestBuilder =
new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI);
if (suggestion.isUntrusted()) {
networkRequestBuilder.removeCapability(NET_CAPABILITY_TRUSTED);
}
if (SdkLevel.isAtLeastS()) {
if (suggestion.isOemPaid()) {
networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PAID);
}
if (suggestion.isOemPrivate()) {
networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PRIVATE);
}
}
NetworkRequest networkRequest = networkRequestBuilder.build();
ConnectivityManager.NetworkCallback networkCallback =
new ConnectivityManager.NetworkCallback();
pw.println("Adding request: " + networkRequest);
mConnectivityManager.requestNetwork(networkRequest, networkCallback);
sActiveRequests.put(
suggestion.getSsid(), Pair.create(networkRequest, networkCallback));
}
return 0;
}
case "remove-suggestion": {
String ssid = getNextArgRequired();
String action = getNextArg();
int actionCode = ACTION_REMOVE_SUGGESTION_DISCONNECT;
if (action != null && action.equals("lingering")) {
actionCode = ACTION_REMOVE_SUGGESTION_LINGER;
}
List<WifiNetworkSuggestion> suggestions =
mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
WifiNetworkSuggestion suggestion = suggestions.stream()
.filter(s -> s.getSsid().equals(ssid))
.findAny()
.orElse(null);
if (suggestion == null) {
pw.println("No matching suggestion to remove");
return -1;
}
mWifiService.removeNetworkSuggestions(
Arrays.asList(suggestion), SHELL_PACKAGE_NAME, actionCode);
// untrusted/oem-paid networks need a corresponding NetworkRequest.
if (suggestion.isUntrusted()
|| (SdkLevel.isAtLeastS()
&& (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
sActiveRequests.remove(suggestion.getSsid());
if (nrAndNc == null) {
pw.println("No matching request to remove");
return -1;
}
pw.println("Removing request: " + nrAndNc.first);
mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
}
return 0;
}
case "remove-all-suggestions":
mWifiService.removeNetworkSuggestions(
Collections.emptyList(), SHELL_PACKAGE_NAME,
WifiManager.ACTION_REMOVE_SUGGESTION_DISCONNECT);
return 0;
case "list-suggestions": {
List<WifiNetworkSuggestion> suggestions =
mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
printWifiNetworkSuggestions(pw, suggestions);
return 0;
}
case "list-all-suggestions": {
Set<WifiNetworkSuggestion> suggestions =
mWifiNetworkSuggestionsManager.getAllNetworkSuggestions();
printWifiNetworkSuggestions(pw, suggestions);
return 0;
}
case "list-suggestions-from-app": {
String packageName = getNextArgRequired();
List<WifiNetworkSuggestion> suggestions =
mWifiService.getNetworkSuggestions(packageName);
printWifiNetworkSuggestions(pw, suggestions);
return 0;
}
case "add-request": {
Pair<String, NetworkRequest> result = buildNetworkRequest(pw);
String ssid = result.first;
NetworkRequest networkRequest = result.second;
ConnectivityManager.NetworkCallback networkCallback =
new ConnectivityManager.NetworkCallback();
pw.println("Adding request: " + networkRequest);
mConnectivityManager.requestNetwork(networkRequest, networkCallback);
sActiveRequests.put(ssid, Pair.create(networkRequest, networkCallback));
return 0;
}
case "remove-request": {
String ssid = getNextArgRequired();
Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
sActiveRequests.remove(ssid);
if (nrAndNc == null) {
pw.println("No matching request to remove");
return -1;
}
pw.println("Removing request: " + nrAndNc.first);
mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
return 0;
}
case "remove-all-requests":
if (sActiveRequests.isEmpty()) {
pw.println("No active requests");
return -1;
}
for (Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc
: sActiveRequests.values()) {
pw.println("Removing request: " + nrAndNc.first);
mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
}
sActiveRequests.clear();
return 0;
case "list-requests":
if (sActiveRequests.isEmpty()) {
pw.println("No active requests");
} else {
pw.println("SSID NetworkRequest");
for (Map.Entry<String,
Pair<NetworkRequest, ConnectivityManager.NetworkCallback>> entry :
sActiveRequests.entrySet()) {
pw.println(String.format("%-32s %-4s",
entry.getKey(), entry.getValue().first));
}
}
return 0;
case "network-requests-set-user-approved": {
String packageName = getNextArgRequired();
boolean approved = getNextArgRequiredTrueOrFalse("yes", "no");
mWifiNetworkFactory.setUserApprovedApp(packageName, approved);
return 0;
}
case "network-requests-has-user-approved": {
String packageName = getNextArgRequired();
boolean hasUserApproved = mWifiNetworkFactory.hasUserApprovedApp(packageName);
pw.println(hasUserApproved ? "yes" : "no");
return 0;
}
case "set-coex-cell-channels": {
if (!SdkLevel.isAtLeastS()) {
return handleDefaultCommands(cmd);
}
mCoexManager.setMockCellChannels(buildCoexCellChannels());
return 0;
}
case "reset-coex-cell-channels": {
if (!SdkLevel.isAtLeastS()) {
return handleDefaultCommands(cmd);
}
mCoexManager.resetMockCellChannels();
return 0;
}
case "get-coex-cell-channels": {
if (!SdkLevel.isAtLeastS()) {
return handleDefaultCommands(cmd);
}
pw.println("Cell channels: " + mCoexManager.getCellChannels());
return 0;
}
case "set-connected-score": {
int score = Integer.parseInt(getNextArgRequired());
CountDownLatch countDownLatch = new CountDownLatch(2);
mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer
WifiScorer connectedScorer = new WifiScorer(mWifiService, countDownLatch);
if (mWifiService.setWifiConnectedNetworkScorer(new Binder(), connectedScorer)) {
// wait for retrieving the session id & score observer.
countDownLatch.await(1000, TimeUnit.MILLISECONDS);
}
if (connectedScorer.getSessionId() == null
|| connectedScorer.getScoreUpdateObserver() == null) {
pw.println("Did not receive session id and/or the score update observer. "
+ "Is the device connected to a wifi network?");
mWifiService.clearWifiConnectedNetworkScorer();
return -1;
}
pw.println("Updating score: " + score + " for session id: "
+ connectedScorer.getSessionId());
try {
connectedScorer.getScoreUpdateObserver().notifyScoreUpdate(
connectedScorer.getSessionId(), score);
} catch (RemoteException e) {
pw.println("Failed to send the score update");
mWifiService.clearWifiConnectedNetworkScorer();
return -1;
}
return 0;
}
case "reset-connected-score": {
mWifiService.clearWifiConnectedNetworkScorer(); // clear any previous scorer
return 0;
}
case "network-suggestions-set-as-carrier-provider": {
String packageName = getNextArgRequired();
boolean enabled = getNextArgRequiredTrueOrFalse("yes", "no");
mWifiNetworkSuggestionsManager
.setAppWorkingAsCrossCarrierProvider(packageName, enabled);
return 0;
}
case "is-network-suggestions-set-as-carrier-provider": {
String packageName = getNextArgRequired();
pw.println(mWifiNetworkSuggestionsManager
.isAppWorkingAsCrossCarrierProvider(packageName) ? "yes" : "no");
return 0;
}
case "remove-shell-app-from-suggestion_database <packageName>": {
String packageName = getNextArgRequired();
mWifiNetworkSuggestionsManager.removeApp(packageName);
return 0;
}
case "set-emergency-callback-mode": {
boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
mActiveModeWarden.emergencyCallbackModeChanged(enabled);
return 0;
}
case "set-emergency-call-state": {
boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
mActiveModeWarden.emergencyCallStateChanged(enabled);
return 0;
}
case "trigger-recovery": {
mSelfRecovery.trigger(REASON_API_CALL);
return 0;
}
case "add-fake-scan": {
String 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;
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;
default:
pw.println("Ignoring unknown option " + dialogOption);
break;
}
dialogOption = getNextOption();
}
ArrayBlockingQueue<String> 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 =
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 <display-id> 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 <display-id> 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<String> 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<Pair<Integer, WorkSource>> details =
mHalDeviceManager.reportImpactToCreateIface(interfaceType,
queryForNewInterface, new WorkSource(uid, packageName));
final SparseArray<String> ifaceMap = new SparseArray<String>() {{
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<Integer, WorkSource> 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 "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;
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 {
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.equals("bridged")) {
if (SdkLevel.isAtLeastS()) {
int[] dualBands = new int[] {
SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ};
configBuilder.setBands(dualBands);
} else {
throw new IllegalArgumentException(
"-b bridged option is not supported before S");
}
} else {
throw new IllegalArgumentException("Invalid band option " + preferredBand);
}
} else if (SdkLevel.isAtLeastT() && option.equals("-x")) {
configBuilder.setWifiSsid(WifiSsid.fromString(ssidStr));
} 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<String, NetworkRequest> 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) {
ScanResult matchingScanResult =
mWifiService.getScanResults(SHELL_PACKAGE_NAME, null)
.stream()
.filter(s -> s.SSID.equals(ssid))
.max(Comparator.comparingInt(s -> s.level))
.orElse(null);
if (matchingScanResult != null) {
bssid = matchingScanResult.BSSID;
} else {
pw.println("No matching bssid found, request will need UI approval");
}
}
if (bssid != null && !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<String, NetworkRequest>(ssidKey,
builder.setNetworkSpecifier(specifierBuilder.build()).build());
}
@RequiresApi(Build.VERSION_CODES.S)
@NonNull
private List<CoexUtils.CoexCellChannel> buildCoexCellChannels() {
List<CoexUtils.CoexCellChannel> cellChannels = new ArrayList<>();
while (getRemainingArgsCount() > 0) {
final @Annotation.NetworkType int rat;
final String ratArg = getNextArgRequired();
if (TextUtils.equals(ratArg, "lte")) {
rat = TelephonyManager.NETWORK_TYPE_LTE;
} else if (TextUtils.equals(ratArg, "nr")) {
rat = TelephonyManager.NETWORK_TYPE_NR;
} else {
throw new IllegalArgumentException("Unknown rat type " + ratArg);
}
final int band = Integer.parseInt(getNextArgRequired());
if (band < 1 || band > 261) {
throw new IllegalArgumentException("Band is " + band
+ " but should be a value from 1 to 261");
}
final int downlinkFreqKhz = Integer.parseInt(getNextArgRequired());
if (downlinkFreqKhz < 0 && downlinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) {
throw new IllegalArgumentException("Downlink frequency is " + downlinkFreqKhz
+ " but should be >= 0 or UNKNOWN: "
+ PhysicalChannelConfig.FREQUENCY_UNKNOWN);
}
final int downlinkBandwidthKhz = Integer.parseInt(getNextArgRequired());
if (downlinkBandwidthKhz <= 0
&& downlinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
throw new IllegalArgumentException("Downlink bandwidth is " + downlinkBandwidthKhz
+ " but should be > 0 or UNKNOWN: "
+ PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN);
}
final int uplinkFreqKhz = Integer.parseInt(getNextArgRequired());
if (uplinkFreqKhz < 0 && uplinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) {
throw new IllegalArgumentException("Uplink frequency is " + uplinkFreqKhz
+ " but should be >= 0 or UNKNOWN: "
+ PhysicalChannelConfig.FREQUENCY_UNKNOWN);
}
final int uplinkBandwidthKhz = Integer.parseInt(getNextArgRequired());
if (uplinkBandwidthKhz <= 0
&& uplinkBandwidthKhz != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
throw new IllegalArgumentException("Uplink bandwidth is " + uplinkBandwidthKhz
+ " but should be > 0 or UNKNOWN: "
+ PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN);
}
cellChannels.add(new CoexUtils.CoexCellChannel(rat, band,
downlinkFreqKhz, downlinkBandwidthKhz, uplinkFreqKhz, uplinkBandwidthKhz,
SubscriptionManager.INVALID_SUBSCRIPTION_ID));
}
return cellChannels;
}
private void setAutoJoin(PrintWriter pw, String ssid, boolean allowAutojoin) {
// For suggestions, this will work only if the config has already been added
// to WifiConfigManager.
Bundle extras = new Bundle();
if (SdkLevel.isAtLeastS()) {
extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
mContext.getAttributionSource());
}
WifiConfiguration retrievedConfig =
mWifiService.getPrivilegedConfiguredNetworks(SHELL_PACKAGE_NAME, null, extras)
.getList()
.stream()
.filter(n -> n.SSID.equals(ssid))
.findAny()
.orElse(null);
if (retrievedConfig == null) {
pw.println("Cannot retrieve config, autojoin setting skipped.");
return;
}
mWifiService.allowAutojoin(retrievedConfig.networkId, allowAutojoin);
}
private int sendLinkProbe(PrintWriter pw) throws InterruptedException {
// Note: should match WifiNl80211Manager#SEND_MGMT_FRAME_TIMEOUT_MS
final int sendMgmtFrameTimeoutMs = 1000;
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
mWifiThreadRunner.post(() ->
mActiveModeWarden.getPrimaryClientModeManager().probeLink(new LinkProbeCallback() {
@Override
public void onAck(int elapsedTimeMs) {
queue.offer("Link probe succeeded after " + elapsedTimeMs + " ms");
}
@Override
public void onFailure(int reason) {
queue.offer("Link probe failed with reason "
+ LinkProbeCallback.failureReasonToString(reason));
}
}, -1));
// block until msg is received, or timed out
String msg = queue.poll(sendMgmtFrameTimeoutMs + 1000, TimeUnit.MILLISECONDS);
if (msg == null) {
pw.println("Link probe timed out");
} else {
pw.println(msg);
}
return 0;
}
private boolean isApChannelMHzValid(PrintWriter pw, int apChannelMHz) {
int[] allowed2gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
int[] allowed5gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
int[] allowed5gDfsFreq =
mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
int[] allowed6gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ);
int[] allowed60gFreq = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_60_GHZ);
if (allowed2gFreq == null) {
allowed2gFreq = new int[0];
}
if (allowed5gFreq == null) {
allowed5gFreq = new int[0];
}
if (allowed5gDfsFreq == null) {
allowed5gDfsFreq = new int[0];
}
if (allowed6gFreq == null) {
allowed6gFreq = new int[0];
}
if (allowed60gFreq == null) {
allowed60gFreq = new int[0];
}
pw.println("2G freq: " + Arrays.toString(allowed2gFreq));
pw.println("5G freq: " + Arrays.toString(allowed5gFreq));
pw.println("5G DFS: " + Arrays.toString(allowed5gDfsFreq));
pw.println("6G freq: " + Arrays.toString(allowed6gFreq));
pw.println("60G freq: " + Arrays.toString(allowed60gFreq));
return (Arrays.binarySearch(allowed2gFreq, apChannelMHz) >= 0
|| Arrays.binarySearch(allowed5gFreq, apChannelMHz) >= 0
|| Arrays.binarySearch(allowed5gDfsFreq, apChannelMHz) >= 0)
|| Arrays.binarySearch(allowed6gFreq, apChannelMHz) >= 0
|| Arrays.binarySearch(allowed60gFreq, apChannelMHz) >= 0;
}
private void waitForWifiEnabled(boolean enabled) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
int state = mWifiService.getWifiEnabledState();
if ((enabled && state == WIFI_STATE_ENABLED)
|| (!enabled && state == WIFI_STATE_DISABLED)) {
countDownLatch.countDown();
}
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mContext.registerReceiver(broadcastReceiver, filter);
mWifiService.setWifiEnabled(SHELL_PACKAGE_NAME, enabled);
countDownLatch.await(5000, TimeUnit.MILLISECONDS);
mContext.unregisterReceiver(broadcastReceiver);
}
private void printWifiInfo(PrintWriter pw, WifiInfo info) {
if (info.getSupplicantState() != SupplicantState.COMPLETED) {
pw.println("Wifi is not connected");
return;
}
pw.println("Wifi is connected to " + info.getSSID());
pw.println("WifiInfo: " + info);
// additional diagnostics not printed by WifiInfo.toString()
pw.println("successfulTxPackets: " + info.txSuccess);
pw.println("successfulTxPacketsPerSecond: " + info.getSuccessfulTxPacketsPerSecond());
pw.println("retriedTxPackets: " + info.txRetries);
pw.println("retriedTxPacketsPerSecond: " + info.getRetriedTxPacketsPerSecond());
pw.println("lostTxPackets: " + info.txBad);
pw.println("lostTxPacketsPerSecond: " + info.getLostTxPacketsPerSecond());
pw.println("successfulRxPackets: " + info.rxSuccess);
pw.println("successfulRxPacketsPerSecond: " + info.getSuccessfulRxPacketsPerSecond());
}
private void printStatus(PrintWriter pw) {
boolean wifiEnabled = mWifiService.getWifiEnabledState() == WIFI_STATE_ENABLED;
pw.println("Wifi is " + (wifiEnabled ? "enabled" : "disabled"));
pw.println("Wifi scanning is "
+ (mWifiService.isScanAlwaysAvailable()
? "always available" : "only available when wifi is enabled"));
if (!wifiEnabled) {
return;
}
if (Binder.getCallingUid() != Process.ROOT_UID) {
// not privileged, just dump the primary client mode manager manager status
// (public API contents).
pw.println("==== Primary ClientModeManager instance ====");
printWifiInfo(pw, mWifiService.getConnectionInfo(SHELL_PACKAGE_NAME, null));
} else {
// privileged, dump out all the client mode manager manager statuses
for (ClientModeManager cm : mActiveModeWarden.getClientModeManagers()) {
pw.println("==== ClientModeManager instance: " + cm + " ====");
WifiInfo info = cm.syncRequestConnectionInfo();
printWifiInfo(pw, info);
if (info.getSupplicantState() != SupplicantState.COMPLETED) {
continue;
}
Network network = cm.syncGetCurrentNetwork();
NetworkCapabilities capabilities =
mConnectivityManager.getNetworkCapabilities(network);
pw.println("NetworkCapabilities: " + capabilities);
}
}
}
private void onHelpNonPrivileged(PrintWriter pw) {
pw.println(" get-country-code");
pw.println(" Gets country code as a two-letter string");
pw.println(" set-wifi-enabled enabled|disabled");
pw.println(" Enables/disables Wifi on this device.");
pw.println(" set-scan-always-available enabled|disabled");
pw.println(" Sets whether scanning should be available even when wifi is off.");
pw.println(" list-scan-results");
pw.println(" Lists the latest scan results");
pw.println(" start-scan");
pw.println(" Start a new scan");
pw.println(" list-networks");
pw.println(" Lists the saved networks");
pw.println(" forget-network <networkId>");
pw.println(" Remove the network mentioned by <networkId>");
pw.println(" - Use list-networks to retrieve <networkId> for the network");
pw.println(" status");
pw.println(" Current wifi status");
pw.println(" set-verbose-logging enabled|disabled ");
pw.println(" Set the verbose logging enabled or disabled");
pw.println(" is-verbose-logging");
pw.println(" Check whether verbose logging enabled or disabled");
pw.println(" start-restricting-auto-join-to-subscription-id subId");
pw.println(" temporarily disable all wifi networks except merged carrier networks with"
+ " the given subId");
pw.println(" stop-restricting-auto-join-to-subscription-id");
pw.println(" Undo the effects of "
+ "start-restricting-auto-join-to-subscription-id");
pw.println(" add-suggestion <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-u] [-o] [-p] [-m] "
+ " [-s] [-d] [-b <bssid>] [-e] [-i] [-a <carrierId>] [-c <subscriptionId>]");
pw.println(" Add a network suggestion with provided params");
pw.println(" Use 'network-suggestions-set-user-approved " + SHELL_PACKAGE_NAME + " yes'"
+ " to approve suggestions added via shell (Needs root access)");
pw.println(" <ssid> - SSID of the network");
pw.println(" open|owe|wpa2|wpa3 - Security type of the network.");
pw.println(" - Use 'open' or 'owe' for networks with no passphrase");
pw.println(" - 'open' - Open networks (Most prevalent)");
pw.println(" - 'owe' - Enhanced open networks");
pw.println(" - Use 'wpa2' or 'wpa3' for networks with passphrase");
pw.println(" - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
pw.println(" - 'wpa3' - WPA-3 PSK networks");
pw.println(" -u - Mark the suggestion untrusted.");
pw.println(" -o - Mark the suggestion oem paid.");
pw.println(" -p - Mark the suggestion oem private.");
pw.println(" -m - Mark the suggestion metered.");
pw.println(" -h - Mark the network hidden.");
pw.println(" -s - Share the suggestion with user.");
pw.println(" -d - Mark the suggestion autojoin disabled.");
pw.println(" -b <bssid> - Set specific BSSID.");
pw.println(" -r - Enable non_persistent randomization (disabled by default)");
pw.println(" -a - Mark the suggestion carrier merged");
pw.println(" -c <carrierId> - set carrier Id");
pw.println(" -i <subscriptionId> - set subscription Id, if -a is used, "
+ "this must be set");
pw.println(" remove-suggestion <ssid> [-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 <bandNumber 1-261> "
+ "<downlinkFreqKhz or UNKNOWN: "
+ PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> "
+ "<downlinkBandwidthKhz or UNKNOWN: "
+ PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + "> "
+ "<uplinkFreqKhz or UNKNOWN: "
+ PhysicalChannelConfig.FREQUENCY_UNKNOWN + "> "
+ "<uplinkBandwidthKhz or UNKNOWN: "
+ PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN + ">] ...");
pw.println(" Sets a list of zero or more cell channels to use for coex calculations."
+ " Actual device reported cell channels will be ignored until"
+ " reset-coex-cell-channels is called.");
pw.println(" reset-coex-cell-channels");
pw.println(" Removes all cell channels set in set-coex-cell-channels and returns to "
+ "listening on actual device reported cell channels");
pw.println(" get-coex-cell-channels");
pw.println(" Prints the cell channels being used for coex.");
}
pw.println(" set-connected-score <score>");
pw.println(" Set connected wifi network score (to choose between LTE & Wifi for "
+ "default route).");
pw.println(" This turns off the active connected scorer (default or external).");
pw.println(" Only works while connected to a wifi network. This score will stay in "
+ "effect until you call reset-connected-score or the device disconnects from the "
+ "current network.");
pw.println(" <score> - Integer score should be in the range of 0 - 60");
pw.println(" reset-connected-score");
pw.println(" Turns on the default connected scorer.");
pw.println(" Note: Will clear any external scorer set.");
pw.println(" start-softap <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition) "
+ "<passphrase> [-b 2|5|6|any|bridged]");
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 - select the preferred band.");
pw.println(" - Use '2' to select 2.4GHz band as the preferred band");
pw.println(" - Use '5' to select 5GHz band as the preferred band");
pw.println(" - Use '6' to select 6GHz band as the preferred band");
pw.println(" - Use 'any' to indicate no band preference");
pw.println(" - Use 'bridged' to indicate bridged AP which enables APs on both "
+ "2.4G + 5G");
pw.println(" Note: If the band option is not provided, 2.4GHz is the preferred band.");
pw.println(" The exact channel is auto-selected by FW unless overridden by "
+ "force-softap-channel command");
pw.println(" -x - Specifies the SSID as hex digits instead of plain text (T and above)");
pw.println(" stop-softap");
pw.println(" Stop softap (hotspot)");
pw.println(" pmksa-flush <networkId>");
pw.println(" - Flush the local PMKSA cache associated with the network id."
+ " Use list-networks to retrieve <networkId> for the network");
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 <title>] [-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(" 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-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.");
}
private void onHelpPrivileged(PrintWriter pw) {
pw.println(" connect-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-x] [-m] [-d] "
+ "[-b <bssid>] [-r auto|none|persistent|non_persistent]");
pw.println(" Connect to a network with provided params and add to saved networks list");
pw.println(" <ssid> - SSID of the network");
pw.println(" open|owe|wpa2|wpa3 - Security type of the network.");
pw.println(" - Use 'open' or 'owe' for networks with no passphrase");
pw.println(" - 'open' - Open networks (Most prevalent)");
pw.println(" - 'owe' - Enhanced open networks");
pw.println(" - Use 'wpa2' or 'wpa3' for networks with passphrase");
pw.println(" - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
pw.println(" - 'wpa3' - WPA-3 PSK networks");
pw.println(" -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 <bssid> - Set specific BSSID.");
pw.println(" -r auto|none|persistent|non_persistent - MAC randomization scheme for the"
+ " network");
pw.println(" add-network <ssid> open|owe|wpa2|wpa3 [<passphrase>] [-x] [-m] [-d] "
+ "[-b <bssid>] [-r auto|none|persistent|non_persistent]");
pw.println(" Add/update saved network with provided params");
pw.println(" <ssid> - SSID of the network");
pw.println(" open|owe|wpa2|wpa3 - Security type of the network.");
pw.println(" - Use 'open' or 'owe' for networks with no passphrase");
pw.println(" - 'open' - Open networks (Most prevalent)");
pw.println(" - 'owe' - Enhanced open networks");
pw.println(" - Use 'wpa2' or 'wpa3' for networks with passphrase");
pw.println(" - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
pw.println(" - 'wpa3' - WPA-3 PSK networks");
pw.println(" -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 <bssid> - Set specific BSSID.");
pw.println(" -r auto|none|persistent|non_persistent - MAC randomization scheme for the"
+ " network");
pw.println(" set-poll-rssi-interval-msecs <int>");
pw.println(" Sets the interval between RSSI polls to <int> milliseconds.");
pw.println(" get-poll-rssi-interval-msecs");
pw.println(" Gets current interval between RSSI polls, in milliseconds.");
pw.println(" force-hi-perf-mode enabled|disabled");
pw.println(" Sets whether hi-perf mode is forced or left for normal operation.");
pw.println(" force-low-latency-mode enabled|disabled");
pw.println(" Sets whether low latency mode is forced or left for normal operation.");
pw.println(" network-suggestions-set-user-approved <package name> yes|no");
pw.println(" Sets whether network suggestions from the app is approved or not.");
pw.println(" network-suggestions-has-user-approved <package name>");
pw.println(" Queries whether network suggestions from the app is approved or not.");
pw.println(" imsi-protection-exemption-set-user-approved-for-carrier <carrier id> yes|no");
pw.println(" Sets whether Imsi protection exemption for carrier is approved or not");
pw.println(" imsi-protection-exemption-has-user-approved-for-carrier <carrier id>");
pw.println(" Queries whether Imsi protection exemption for carrier is approved or not");
pw.println(" imsi-protection-exemption-clear-user-approved-for-carrier <carrier id>");
pw.println(" Clear the user choice on Imsi protection exemption for carrier");
pw.println(" network-requests-remove-user-approved-access-points <package name>");
pw.println(" Removes all user approved network requests for the app.");
pw.println(" clear-user-disabled-networks");
pw.println(" Clears the user disabled networks list.");
pw.println(" send-link-probe");
pw.println(" Manually triggers a link probe.");
pw.println(" force-softap-band enabled <int> | disabled");
pw.println(" Forces soft AP band to 2|5|6");
pw.println(" force-softap-channel enabled <int> | disabled");
pw.println(" Sets whether soft AP channel is forced to <int> MHz");
pw.println(" or left for normal operation.");
pw.println(" force-country-code enabled <two-letter code> | disabled ");
pw.println(" Sets country code to <two-letter code> or left for normal value");
pw.println(" 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(" get-softap-supported-features");
pw.println(" Gets softap supported features. Will print 'wifi_softap_acs_supported'");
pw.println(" and/or 'wifi_softap_wpa3_sae_supported',");
pw.println(" and/or 'wifi_softap_bridged_ap_supported',");
pw.println(" and/or 'wifi_softap_bridged_ap_with_sta_supported',");
pw.println(" each on a separate line.");
pw.println(" settings-reset");
pw.println(" Initiates wifi settings reset");
pw.println(" add-request [-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(" 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]");
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 - select the preferred band.");
pw.println(" - Use '2' to select 2.4GHz band as the preferred band");
pw.println(" - Use '5' to select 5GHz band as the preferred band");
pw.println(" - Use '6' to select 6GHz band as the preferred band");
pw.println(" - Use 'any' to indicate no band preference");
pw.println(" - Use 'bridged' to indicate bridged AP which enables APs on both "
+ "2.4G + 5G");
pw.println(" Note: If the band option is not provided, 2.4GHz is the preferred band.");
pw.println(" 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.");
}
@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("/"))));
}
}
}
}