/* * Copyright (C) 2008 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 android.net.wifi; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.ACCESS_WIFI_STATE; import static android.Manifest.permission.CHANGE_WIFI_STATE; import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION; import static android.Manifest.permission.NEARBY_WIFI_DEVICES; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_SETUP_WIZARD; import static android.Manifest.permission.READ_WIFI_CREDENTIAL; import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.StringDef; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.ActivityManager; import android.app.admin.WifiSsidPolicy; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.DhcpInfo; import android.net.DhcpOption; import android.net.LinkProperties; import android.net.MacAddress; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.NetworkStack; import android.net.TetheringManager; import android.net.Uri; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.ProvisioningCallback; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDiscoveryConfig; import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.twt.TwtRequest; import android.net.wifi.twt.TwtSession; import android.net.wifi.twt.TwtSessionCallback; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.WorkSource; import android.os.connectivity.WifiActivityEnergyInfo; import android.telephony.SubscriptionInfo; import android.text.TextUtils; import android.util.ArraySet; import android.util.CloseGuard; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import androidx.annotation.RequiresApi; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.HandlerExecutor; import com.android.modules.utils.ParceledListSlice; import com.android.modules.utils.build.SdkLevel; import com.android.wifi.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.net.InetAddress; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.Executor; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.IntConsumer; /** * This class provides the primary API for managing all aspects of Wi-Fi * connectivity. *

* On releases before {@link android.os.Build.VERSION_CODES#N}, this object * should only be obtained from an {@linkplain Context#getApplicationContext() * application context}, and not from any other derived context to avoid memory * leaks within the calling process. *

* It deals with several categories of items: *

* *

* This is the API to use when performing Wi-Fi specific operations. To perform * operations that pertain to network connectivity at an abstract level, use * {@link android.net.ConnectivityManager}. *

*/ @SystemService(Context.WIFI_SERVICE) public class WifiManager { private static final String TAG = "WifiManager"; /** * Local networks should not be modified by B&R since the user may have * updated it with the latest configurations. * @hide */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2) public static final long NOT_OVERRIDE_EXISTING_NETWORKS_ON_RESTORE = 234793325L; // Supplicant error codes: /** * The error code if there was a problem authenticating. * @deprecated This is no longer supported. */ @Deprecated public static final int ERROR_AUTHENTICATING = 1; /** * The reason code if there is no error during authentication. * It could also imply that there no authentication in progress, * this reason code also serves as a reset value. * @deprecated This is no longer supported. * @hide */ @Deprecated public static final int ERROR_AUTH_FAILURE_NONE = 0; /** * The reason code if there was a timeout authenticating. * @deprecated This is no longer supported. * @hide */ @Deprecated public static final int ERROR_AUTH_FAILURE_TIMEOUT = 1; /** * The reason code if there was a wrong password while * authenticating. * @deprecated This is no longer supported. * @hide */ @Deprecated public static final int ERROR_AUTH_FAILURE_WRONG_PSWD = 2; /** * The reason code if there was EAP failure while * authenticating. * @deprecated This is no longer supported. * @hide */ @Deprecated public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3; /** @hide */ public static final int NETWORK_SUGGESTIONS_MAX_PER_APP_LOW_RAM = 256; /** @hide */ public static final int NETWORK_SUGGESTIONS_MAX_PER_APP_HIGH_RAM = 1024; /** * Reason code if all of the network suggestions were successfully added or removed. */ public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; /** * Reason code if there was an internal error in the platform while processing the addition or * removal of suggestions. */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; /** * Reason code if the user has disallowed "android:change_wifi_state" app-ops from the app. * @see android.app.AppOpsManager#unsafeCheckOp(String, int, String). */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2; /** * Reason code if one or more of the network suggestions added already exists in platform's * database. * Note: this code will not be returned with Android 11 as in-place modification is allowed, * please check {@link #addNetworkSuggestions(List)}. * @see WifiNetworkSuggestion#equals(Object) */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; /** * Reason code if the number of network suggestions provided by the app crosses the max * threshold set per app. * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} if * the total size exceeds the limit. * @see #getMaxNumberOfNetworkSuggestionsPerApp() */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; /** * Reason code if one or more of the network suggestions removed does not exist in platform's * database. * The framework won't remove any suggestions if one or more of suggestions provided * by {@link #removeNetworkSuggestions(List)} does not exist in database. * @see WifiNetworkSuggestion#equals(Object) */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; /** * Reason code if one or more of the network suggestions added is not allowed. * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} * if one or more of them is not allowed. * This error may be caused by suggestion is using SIM-based encryption method, but calling app * is not carrier privileged. */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED = 6; /** * Reason code if one or more of the network suggestions added is invalid. Framework will reject * all the suggestions in the list. * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} * if one or more of them is invalid. * Please use {@link WifiNetworkSuggestion.Builder} to create network suggestions. */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID = 7; /** * Reason code if {@link android.os.UserManager#DISALLOW_ADD_WIFI_CONFIG} user restriction * is set and calling app is restricted by device admin. */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_RESTRICTED_BY_ADMIN = 8; /** @hide */ @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = { STATUS_NETWORK_SUGGESTIONS_SUCCESS, STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL, STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED, STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE, STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP, STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID, STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED, STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID, STATUS_NETWORK_SUGGESTIONS_ERROR_RESTRICTED_BY_ADMIN, }) @Retention(RetentionPolicy.SOURCE) public @interface NetworkSuggestionsStatusCode {} /** * Reason code if suggested network connection attempt failed with an unknown failure. */ public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_UNKNOWN = 0; /** * Reason code if suggested network connection attempt failed with association failure. */ public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION = 1; /** * Reason code if suggested network connection attempt failed with an authentication failure. */ public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION = 2; /** * Reason code if suggested network connection attempt failed with an IP provision failure. */ public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING = 3; /** @hide */ @IntDef(prefix = {"STATUS_SUGGESTION_CONNECTION_FAILURE_"}, value = {STATUS_SUGGESTION_CONNECTION_FAILURE_UNKNOWN, STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION, STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION, STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING }) @Retention(RetentionPolicy.SOURCE) public @interface SuggestionConnectionStatusCode {} /** * Reason code if local-only network connection attempt failed with an unknown failure. */ public static final int STATUS_LOCAL_ONLY_CONNECTION_FAILURE_UNKNOWN = 0; /** * Reason code if local-only network connection attempt failed with association failure. */ public static final int STATUS_LOCAL_ONLY_CONNECTION_FAILURE_ASSOCIATION = 1; /** * Reason code if local-only network connection attempt failed with an authentication failure. */ public static final int STATUS_LOCAL_ONLY_CONNECTION_FAILURE_AUTHENTICATION = 2; /** * Reason code if local-only network connection attempt failed with an IP provisioning failure. */ public static final int STATUS_LOCAL_ONLY_CONNECTION_FAILURE_IP_PROVISIONING = 3; /** * Reason code if local-only network connection attempt failed with AP not in range. */ public static final int STATUS_LOCAL_ONLY_CONNECTION_FAILURE_NOT_FOUND = 4; /** * Reason code if local-only network connection attempt failed with AP not responding */ public static final int STATUS_LOCAL_ONLY_CONNECTION_FAILURE_NO_RESPONSE = 5; /** @hide */ @IntDef(prefix = {"STATUS_LOCAL_ONLY_CONNECTION_FAILURE_"}, value = {STATUS_LOCAL_ONLY_CONNECTION_FAILURE_UNKNOWN, STATUS_LOCAL_ONLY_CONNECTION_FAILURE_ASSOCIATION, STATUS_LOCAL_ONLY_CONNECTION_FAILURE_AUTHENTICATION, STATUS_LOCAL_ONLY_CONNECTION_FAILURE_IP_PROVISIONING, STATUS_LOCAL_ONLY_CONNECTION_FAILURE_NOT_FOUND, STATUS_LOCAL_ONLY_CONNECTION_FAILURE_NO_RESPONSE }) @Retention(RetentionPolicy.SOURCE) public @interface LocalOnlyConnectionStatusCode {} /** * Status code if suggestion approval status is unknown, an App which hasn't made any * suggestions will get this code. */ public static final int STATUS_SUGGESTION_APPROVAL_UNKNOWN = 0; /** * Status code if the calling app is still pending user approval for suggestions. */ public static final int STATUS_SUGGESTION_APPROVAL_PENDING = 1; /** * Status code if the calling app got the user approval for suggestions. */ public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER = 2; /** * Status code if the calling app suggestions were rejected by the user. */ public static final int STATUS_SUGGESTION_APPROVAL_REJECTED_BY_USER = 3; /** * Status code if the calling app was approved by virtue of being a carrier privileged app. * * @see android.telephony.TelephonyManager#hasCarrierPrivileges() */ public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE = 4; /** @hide */ @IntDef(prefix = {"STATUS_SUGGESTION_APPROVAL_"}, value = {STATUS_SUGGESTION_APPROVAL_UNKNOWN, STATUS_SUGGESTION_APPROVAL_PENDING, STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER, STATUS_SUGGESTION_APPROVAL_REJECTED_BY_USER, STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE }) @Retention(RetentionPolicy.SOURCE) public @interface SuggestionUserApprovalStatus {} /** * Disable PNO scan until device reboot. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public static final int PNO_SCAN_STATE_DISABLED_UNTIL_REBOOT = 0; /** * Disable PNO scan until device reboot or Wi-Fi is toggled. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public static final int PNO_SCAN_STATE_DISABLED_UNTIL_WIFI_TOGGLE = 1; /** * Enable PNO scan. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public static final int PNO_SCAN_STATE_ENABLED = 2; /** @hide */ @IntDef(prefix = {"PNO_SCAN_STATE_"}, value = {PNO_SCAN_STATE_DISABLED_UNTIL_REBOOT, PNO_SCAN_STATE_DISABLED_UNTIL_WIFI_TOGGLE, PNO_SCAN_STATE_ENABLED }) @Retention(RetentionPolicy.SOURCE) public @interface PnoScanState {} /** * If one of the removed suggestions is currently connected, that network will be disconnected * after a short delay as opposed to immediately (which will be done by * {@link #ACTION_REMOVE_SUGGESTION_DISCONNECT}). The {@link ConnectivityManager} may call the * {@link NetworkCallback#onLosing(Network, int)} on such networks. */ public static final int ACTION_REMOVE_SUGGESTION_LINGER = 1; /** * If one of the removed suggestions is currently connected, trigger an immediate disconnect * after suggestions removal */ public static final int ACTION_REMOVE_SUGGESTION_DISCONNECT = 2; /** @hide */ @IntDef(prefix = {"ACTION_REMOVE_SUGGESTION_"}, value = {ACTION_REMOVE_SUGGESTION_LINGER, ACTION_REMOVE_SUGGESTION_DISCONNECT }) @Retention(RetentionPolicy.SOURCE) public @interface ActionAfterRemovingSuggestion {} /** * Only available on Android S or later. * @hide **/ public static final String EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE = "EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE"; /** * Broadcast intent action indicating whether Wi-Fi scanning is currently available. * Available extras: * - {@link #EXTRA_SCAN_AVAILABLE} */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_WIFI_SCAN_AVAILABILITY_CHANGED = "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED"; /** * A boolean extra indicating whether scanning is currently available. * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABILITY_CHANGED}. * Its value is true if scanning is currently available, false otherwise. */ public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE"; /** * Broadcast intent action indicating that the credential of a Wi-Fi network * has been changed. One extra provides the ssid of the network. Another * extra provides the event type, whether the credential is saved or forgot. * @hide */ @SystemApi public static final String WIFI_CREDENTIAL_CHANGED_ACTION = "android.net.wifi.WIFI_CREDENTIAL_CHANGED"; /** @hide */ @SystemApi public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et"; /** @hide */ @SystemApi public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid"; /** @hide */ @SystemApi public static final int WIFI_CREDENTIAL_SAVED = 0; /** @hide */ @SystemApi public static final int WIFI_CREDENTIAL_FORGOT = 1; /** @hide */ @SystemApi public static final int PASSPOINT_HOME_NETWORK = 0; /** @hide */ @SystemApi public static final int PASSPOINT_ROAMING_NETWORK = 1; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { API_SCANNING_ENABLED, API_WIFI_ENABLED, API_SOFT_AP, API_TETHERED_HOTSPOT, API_AUTOJOIN_GLOBAL, API_SET_SCAN_SCHEDULE, API_SET_ONE_SHOT_SCREEN_ON_CONNECTIVITY_SCAN_DELAY, API_SET_NETWORK_SELECTION_CONFIG, API_SET_THIRD_PARTY_APPS_ENABLING_WIFI_CONFIRMATION_DIALOG, API_ADD_NETWORK, API_UPDATE_NETWORK, API_ALLOW_AUTOJOIN, API_CONNECT_CONFIG, API_CONNECT_NETWORK_ID, API_DISABLE_NETWORK, API_ENABLE_NETWORK, API_FORGET, API_SAVE, API_START_SCAN, API_START_LOCAL_ONLY_HOTSPOT, API_P2P_DISCOVER_PEERS, API_P2P_DISCOVER_PEERS_ON_SOCIAL_CHANNELS, API_P2P_DISCOVER_PEERS_ON_SPECIFIC_FREQUENCY, API_P2P_STOP_PEER_DISCOVERY, API_P2P_CONNECT, API_P2P_CANCEL_CONNECT, API_P2P_CREATE_GROUP, API_P2P_CREATE_GROUP_P2P_CONFIG, API_P2P_REMOVE_GROUP, API_P2P_START_LISTENING, API_P2P_STOP_LISTENING, API_P2P_SET_CHANNELS, API_WIFI_SCANNER_START_SCAN, API_SET_TDLS_ENABLED, API_SET_TDLS_ENABLED_WITH_MAC_ADDRESS, API_P2P_DISCOVER_PEERS_WITH_CONFIG_PARAMS }) public @interface ApiType {} /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiScanner#setScanningEnabled(boolean)} * @hide */ @SystemApi public static final int API_SCANNING_ENABLED = 1; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiManager#setWifiEnabled(boolean)} . * @hide */ @SystemApi public static final int API_WIFI_ENABLED = 2; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiManager#startSoftAp(WifiConfiguration)} and * {@link WifiManager#stopSoftAp()}. * @hide */ @SystemApi public static final int API_SOFT_AP = 3; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiManager#startTetheredHotspot(SoftApConfiguration)}. * @hide */ @SystemApi public static final int API_TETHERED_HOTSPOT = 4; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiManager#allowAutojoinGlobal(boolean)}. * @hide */ @SystemApi public static final int API_AUTOJOIN_GLOBAL = 5; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiManager#setScreenOnScanSchedule(List)}. * @hide */ @SystemApi public static final int API_SET_SCAN_SCHEDULE = 6; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiManager#setOneShotScreenOnConnectivityScanDelayMillis(int)}. * @hide */ @SystemApi public static final int API_SET_ONE_SHOT_SCREEN_ON_CONNECTIVITY_SCAN_DELAY = 7; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#setNetworkSelectionConfig(WifiNetworkSelectionConfig)} * @hide */ @SystemApi public static final int API_SET_NETWORK_SELECTION_CONFIG = 8; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#setThirdPartyAppEnablingWifiConfirmationDialogEnabled(boolean)} * @hide */ @SystemApi public static final int API_SET_THIRD_PARTY_APPS_ENABLING_WIFI_CONFIRMATION_DIALOG = 9; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#addNetwork(WifiConfiguration)} * @hide */ @SystemApi public static final int API_ADD_NETWORK = 10; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#updateNetwork(WifiConfiguration)} * @hide */ @SystemApi public static final int API_UPDATE_NETWORK = 11; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#allowAutojoin(int, boolean)} * @hide */ @SystemApi public static final int API_ALLOW_AUTOJOIN = 12; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#connect(WifiConfiguration, ActionListener)} * @hide */ @SystemApi public static final int API_CONNECT_CONFIG = 13; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#connect(int, ActionListener)} * @hide */ @SystemApi public static final int API_CONNECT_NETWORK_ID = 14; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#disableNetwork(int)} * @hide */ @SystemApi public static final int API_DISABLE_NETWORK = 15; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#enableNetwork(int, boolean)} * @hide */ @SystemApi public static final int API_ENABLE_NETWORK = 16; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#forget(int, ActionListener)} * @hide */ @SystemApi public static final int API_FORGET = 17; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#save(WifiConfiguration, ActionListener)} * @hide */ @SystemApi public static final int API_SAVE = 18; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#startScan()} * @hide */ @SystemApi public static final int API_START_SCAN = 19; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)} * @hide */ @SystemApi public static final int API_START_LOCAL_ONLY_HOTSPOT = 20; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#discoverPeers(WifiP2pManager.Channel, WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_DISCOVER_PEERS = 21; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#discoverPeersOnSocialChannels(WifiP2pManager.Channel, * WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_DISCOVER_PEERS_ON_SOCIAL_CHANNELS = 22; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#discoverPeersOnSpecificFrequency(WifiP2pManager.Channel, int, * WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_DISCOVER_PEERS_ON_SPECIFIC_FREQUENCY = 23; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#stopPeerDiscovery(WifiP2pManager.Channel, * WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_STOP_PEER_DISCOVERY = 24; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig, * WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_CONNECT = 25; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#cancelConnect(WifiP2pManager.Channel, WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_CANCEL_CONNECT = 26; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#createGroup(WifiP2pManager.Channel, WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_CREATE_GROUP = 27; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#createGroup(WifiP2pManager.Channel, WifiP2pConfig, * WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_CREATE_GROUP_P2P_CONFIG = 28; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#removeGroup(WifiP2pManager.Channel, WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_REMOVE_GROUP = 29; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#startListening(WifiP2pManager.Channel, WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_START_LISTENING = 30; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#stopListening(WifiP2pManager.Channel, WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_STOP_LISTENING = 31; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiP2pManager#setWifiP2pChannels(WifiP2pManager.Channel, int, int, * WifiP2pManager.ActionListener)} * @hide */ @SystemApi public static final int API_P2P_SET_CHANNELS = 32; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiScanner#startScan(WifiScanner.ScanSettings, WifiScanner.ScanListener)} * @hide */ @SystemApi public static final int API_WIFI_SCANNER_START_SCAN = 33; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#setTdlsEnabled(InetAddress, boolean)} and * {@link WifiManager#setTdlsEnabled(InetAddress, boolean, Executor, Consumer)} * @hide */ @SystemApi public static final int API_SET_TDLS_ENABLED = 34; /** * A constant used in * {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of * {@link WifiManager#setTdlsEnabledWithMacAddress(String, boolean)} and * {@link WifiManager#setTdlsEnabledWithMacAddress(String, boolean, Executor, Consumer)} * @hide */ @SystemApi public static final int API_SET_TDLS_ENABLED_WITH_MAC_ADDRESS = 35; /** * A constant used in {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiManager#setPnoScanState(int)} * * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public static final int API_SET_PNO_SCAN_ENABLED = 36; /** * A constant used in {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)} * Tracks usage of {@link WifiP2pManager#discoverPeersWithConfigParams( * WifiP2pManager.Channel, WifiP2pDiscoveryConfig, WifiP2pManager.ActionListener)} * * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public static final int API_P2P_DISCOVER_PEERS_WITH_CONFIG_PARAMS = 37; /** * Used internally to keep track of boundary. * @hide */ public static final int API_MAX = 38; /** * Broadcast intent action indicating that a Passpoint provider icon has been received. * * Included extras: * {@link #EXTRA_BSSID_LONG} * {@link #EXTRA_FILENAME} * {@link #EXTRA_ICON} * * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE * *

Note: The broadcast is only delivered to registered receivers - no manifest registered * components will be launched. * * @hide */ public static final String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON"; /** * BSSID of an AP in long representation. The {@link #EXTRA_BSSID} contains BSSID in * String representation. * * Retrieve with {@link android.content.Intent#getLongExtra(String, long)}. * * @hide */ public static final String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG"; /** * Icon data. * * Retrieve with {@link android.content.Intent#getParcelableExtra(String)} and cast into * {@link android.graphics.drawable.Icon}. * * @hide */ public static final String EXTRA_ICON = "android.net.wifi.extra.ICON"; /** * Name of a file. * * Retrieve with {@link android.content.Intent#getStringExtra(String)}. * * @hide */ public static final String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME"; /** * Broadcast intent action indicating a Passpoint OSU Providers List element has been received. * * Included extras: * {@link #EXTRA_BSSID_LONG} * {@link #EXTRA_ANQP_ELEMENT_DATA} * * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE * *

Note: The broadcast is only delivered to registered receivers - no manifest registered * components will be launched. * * @hide */ public static final String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST"; /** * Raw binary data of an ANQP (Access Network Query Protocol) element. * * Retrieve with {@link android.content.Intent#getByteArrayExtra(String)}. * * @hide */ public static final String EXTRA_ANQP_ELEMENT_DATA = "android.net.wifi.extra.ANQP_ELEMENT_DATA"; /** * Broadcast intent action indicating that a Passpoint Deauth Imminent frame has been received. * * Included extras: * {@link #EXTRA_BSSID_LONG} * {@link #EXTRA_ESS} * {@link #EXTRA_DELAY} * {@link #EXTRA_URL} * * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE * *

Note: The broadcast is only delivered to registered receivers - no manifest registered * components will be launched. * * @hide */ public static final String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT"; /** * Flag indicating BSS (Basic Service Set) or ESS (Extended Service Set). This will be set to * {@code true} for ESS. * * Retrieve with {@link android.content.Intent#getBooleanExtra(String, boolean)}. * * @hide */ public static final String EXTRA_ESS = "android.net.wifi.extra.ESS"; /** * Delay in seconds. * * Retrieve with {@link android.content.Intent#getIntExtra(String, int)}. * * @hide */ public static final String EXTRA_DELAY = "android.net.wifi.extra.DELAY"; /** * Broadcast intent action indicating a Passpoint subscription remediation frame has been * received. * * Included extras: * {@link #EXTRA_BSSID_LONG} * {@link #EXTRA_SUBSCRIPTION_REMEDIATION_METHOD} * {@link #EXTRA_URL} * * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE * *

Note: The broadcast is only delivered to registered receivers - no manifest registered * components will be launched. * * @hide */ public static final String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION"; /** * The protocol supported by the subscription remediation server. The possible values are: * 0 - OMA DM * 1 - SOAP XML SPP * * Retrieve with {@link android.content.Intent#getIntExtra(String, int)}. * * @hide */ public static final String EXTRA_SUBSCRIPTION_REMEDIATION_METHOD = "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD"; /** * Activity Action: Receiver should launch Passpoint OSU (Online Sign Up) view. * Included extras: * * {@link #EXTRA_OSU_NETWORK}: {@link Network} instance associated with OSU AP. * {@link #EXTRA_URL}: String representation of a server URL used for OSU process. * * @hide */ @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PASSPOINT_LAUNCH_OSU_VIEW = "android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW"; /** * The lookup key for a {@link android.net.Network} associated with a Passpoint OSU server. * Included in the {@link #ACTION_PASSPOINT_LAUNCH_OSU_VIEW} broadcast. * * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. * * @hide */ @SystemApi public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK"; /** * String representation of an URL for Passpoint OSU. * Included in the {@link #ACTION_PASSPOINT_LAUNCH_OSU_VIEW} broadcast. * * Retrieve with {@link android.content.Intent#getStringExtra(String)}. * * @hide */ @SystemApi public static final String EXTRA_URL = "android.net.wifi.extra.URL"; /** * Broadcast intent action indicating that Wi-Fi has been enabled, disabled, * enabling, disabling, or unknown. One extra provides this state as an int. * Another extra provides the previous state, if available. No network-related * permissions are required to subscribe to this broadcast. * *

This broadcast is not delivered to manifest receivers in * applications that target API version 26 or later. * * @see #EXTRA_WIFI_STATE * @see #EXTRA_PREVIOUS_WIFI_STATE */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED"; /** * The lookup key for an int that indicates whether Wi-Fi is enabled, * disabled, enabling, disabling, or unknown. Retrieve it with * {@link android.content.Intent#getIntExtra(String,int)}. * * @see #WIFI_STATE_DISABLED * @see #WIFI_STATE_DISABLING * @see #WIFI_STATE_ENABLED * @see #WIFI_STATE_ENABLING * @see #WIFI_STATE_UNKNOWN */ public static final String EXTRA_WIFI_STATE = "wifi_state"; /** * The previous Wi-Fi state. * * @see #EXTRA_WIFI_STATE */ public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; /** * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if * it finishes successfully. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_DISABLING = 0; /** * Wi-Fi is disabled. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_DISABLED = 1; /** * Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if * it finishes successfully. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_ENABLING = 2; /** * Wi-Fi is enabled. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_ENABLED = 3; /** * Wi-Fi is in an unknown state. This state will occur when an error happens while enabling * or disabling. * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_UNKNOWN = 4; /** * Broadcast intent action indicating that Wi-Fi AP has been enabled, disabled, * enabling, disabling, or failed. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED"; /** * The lookup key for an int that indicates whether Wi-Fi AP is enabled, * disabled, enabling, disabling, or failed. Retrieve it with * {@link android.content.Intent#getIntExtra(String,int)}. * * @see #WIFI_AP_STATE_DISABLED * @see #WIFI_AP_STATE_DISABLING * @see #WIFI_AP_STATE_ENABLED * @see #WIFI_AP_STATE_ENABLING * @see #WIFI_AP_STATE_FAILED * * @hide */ @SystemApi public static final String EXTRA_WIFI_AP_STATE = "wifi_state"; /** * An extra containing the int error code for Soft AP start failure. * Can be obtained from the {@link #WIFI_AP_STATE_CHANGED_ACTION} using * {@link android.content.Intent#getIntExtra}. * This extra will only be attached if {@link #EXTRA_WIFI_AP_STATE} is * attached and is equal to {@link #WIFI_AP_STATE_FAILED}. * * The error code will be one of: * {@link #SAP_START_FAILURE_GENERAL}, * {@link #SAP_START_FAILURE_NO_CHANNEL}, * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION} * {@link #SAP_START_FAILURE_USER_REJECTED} * * @hide */ @SystemApi public static final String EXTRA_WIFI_AP_FAILURE_REASON = "android.net.wifi.extra.WIFI_AP_FAILURE_REASON"; /** * The previous Wi-Fi state. * * @see #EXTRA_WIFI_AP_STATE * * @hide */ @SystemApi public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; /** * The lookup key for a String extra that stores the interface name used for the Soft AP. * This extra is included in the broadcast {@link #WIFI_AP_STATE_CHANGED_ACTION}. * Retrieve its value with {@link android.content.Intent#getStringExtra(String)}. * * @hide */ @SystemApi public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME"; /** * The lookup key for an int extra that stores the intended IP mode for this Soft AP. * One of {@link #IFACE_IP_MODE_TETHERED} or {@link #IFACE_IP_MODE_LOCAL_ONLY}. * This extra is included in the broadcast {@link #WIFI_AP_STATE_CHANGED_ACTION}. * Retrieve its value with {@link android.content.Intent#getIntExtra(String, int)}. * * @hide */ @SystemApi public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE"; /** @hide */ @IntDef(flag = false, prefix = { "WIFI_AP_STATE_" }, value = { WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_ENABLING, WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_FAILED, }) @Retention(RetentionPolicy.SOURCE) public @interface WifiApState {} /** * Wi-Fi AP is currently being disabled. The state will change to * {@link #WIFI_AP_STATE_DISABLED} if it finishes successfully. * * @see #WIFI_AP_STATE_CHANGED_ACTION * @see #getWifiApState() * * @hide */ @SystemApi public static final int WIFI_AP_STATE_DISABLING = 10; /** * Wi-Fi AP is disabled. * * @see #WIFI_AP_STATE_CHANGED_ACTION * @see #getWifiState() * * @hide */ @SystemApi public static final int WIFI_AP_STATE_DISABLED = 11; /** * Wi-Fi AP is currently being enabled. The state will change to * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully. * * @see #WIFI_AP_STATE_CHANGED_ACTION * @see #getWifiApState() * * @hide */ @SystemApi public static final int WIFI_AP_STATE_ENABLING = 12; /** * Wi-Fi AP is enabled. * * @see #WIFI_AP_STATE_CHANGED_ACTION * @see #getWifiApState() * * @hide */ @SystemApi public static final int WIFI_AP_STATE_ENABLED = 13; /** * Wi-Fi AP is in a failed state. This state will occur when an error occurs during * enabling or disabling * * @see #WIFI_AP_STATE_CHANGED_ACTION * @see #getWifiApState() * * @hide */ @SystemApi public static final int WIFI_AP_STATE_FAILED = 14; /** @hide */ @IntDef(flag = false, prefix = { "SAP_START_FAILURE_" }, value = { SAP_START_FAILURE_GENERAL, SAP_START_FAILURE_NO_CHANNEL, SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION, SAP_START_FAILURE_USER_REJECTED, }) @Retention(RetentionPolicy.SOURCE) public @interface SapStartFailure {} /** * All other reasons for AP start failure besides {@link #SAP_START_FAILURE_NO_CHANNEL}, * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}, and * {@link #SAP_START_FAILURE_USER_REJECTED}. * * @hide */ @SystemApi public static final int SAP_START_FAILURE_GENERAL= 0; /** * If Wi-Fi AP start failed, this reason code means that no legal channel exists on user * selected band due to regulatory constraints. * * @hide */ @SystemApi public static final int SAP_START_FAILURE_NO_CHANNEL = 1; /** * If Wi-Fi AP start failed, this reason code means that the specified configuration * is not supported by the current HAL version. * * @hide */ @SystemApi public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2; /** * If Wi-Fi AP start failed, this reason code means that the user was asked for confirmation to * create the AP and the user declined. * * @hide */ @SystemApi public static final int SAP_START_FAILURE_USER_REJECTED = 3; /** @hide */ @IntDef(flag = false, prefix = { "SAP_CLIENT_BLOCKED_REASON_" }, value = { SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER, SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS, }) @Retention(RetentionPolicy.SOURCE) public @interface SapClientBlockedReason {} /** * If Soft Ap client is blocked, this reason code means that client doesn't exist in the * specified configuration {@link SoftApConfiguration.Builder#setBlockedClientList(List)} * and {@link SoftApConfiguration.Builder#setAllowedClientList(List)} * and the {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)} * is configured as well. * @hide */ @SystemApi public static final int SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0; /** * If Soft Ap client is blocked, this reason code means that no more clients can be * associated to this AP since it reached maximum capacity. The maximum capacity is * the minimum of {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} and * {@link SoftApCapability#getMaxSupportedClients} which get from * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)}. * * @hide */ @SystemApi public static final int SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1; /** * Client disconnected for unspecified reason. This could for example be because the AP is being * shut down. * @hide */ public static final int SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED = 2; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"IFACE_IP_MODE_"}, value = { IFACE_IP_MODE_UNSPECIFIED, IFACE_IP_MODE_CONFIGURATION_ERROR, IFACE_IP_MODE_TETHERED, IFACE_IP_MODE_LOCAL_ONLY}) public @interface IfaceIpMode {} /** * Interface IP mode unspecified. * * @see #updateInterfaceIpState(String, int) * * @hide */ @SystemApi public static final int IFACE_IP_MODE_UNSPECIFIED = -1; /** * Interface IP mode for configuration error. * * @see #updateInterfaceIpState(String, int) * * @hide */ @SystemApi public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; /** * Interface IP mode for tethering. * * @see #updateInterfaceIpState(String, int) * * @hide */ @SystemApi public static final int IFACE_IP_MODE_TETHERED = 1; /** * Interface IP mode for Local Only Hotspot. * * @see #updateInterfaceIpState(String, int) * * @hide */ @SystemApi public static final int IFACE_IP_MODE_LOCAL_ONLY = 2; /** * Broadcast intent action indicating that the wifi network settings * had been reset. * * Note: This intent is sent as a directed broadcast to each manifest registered receiver. * Intent will not be received by dynamically registered receivers. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public static final String ACTION_NETWORK_SETTINGS_RESET = "android.net.wifi.action.NETWORK_SETTINGS_RESET"; /** * Broadcast intent action indicating that the wifi network profiles provisioned * may need refresh. * * Note: This intent is sent as a directed broadcast to each manifest registered receiver; * And restricted to those apps which have the NETWORK_CARRIER_PROVISIONING permission. * Intent will not be received by dynamically registered receivers. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public static final String ACTION_REFRESH_USER_PROVISIONING = "android.net.wifi.action.REFRESH_USER_PROVISIONING"; /** * Broadcast intent action indicating that a connection to the supplicant has * been established (and it is now possible * to perform Wi-Fi operations) or the connection to the supplicant has been * lost. One extra provides the connection state as a boolean, where {@code true} * means CONNECTED. * @deprecated This is no longer supported. * @see #EXTRA_SUPPLICANT_CONNECTED */ @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE"; /** * The lookup key for a boolean that indicates whether a connection to * the supplicant daemon has been gained or lost. {@code true} means * a connection now exists. * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}. * @deprecated This is no longer supported. */ @Deprecated public static final String EXTRA_SUPPLICANT_CONNECTED = "connected"; /** * Broadcast intent action indicating that the state of Wi-Fi connectivity * has changed. An extra provides the new state * in the form of a {@link android.net.NetworkInfo} object. No network-related * permissions are required to subscribe to this broadcast. * *

This broadcast is not delivered to manifest receivers in * applications that target API version 26 or later. * @see #EXTRA_NETWORK_INFO */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE"; /** * The lookup key for a {@link android.net.NetworkInfo} object associated with the * Wi-Fi network. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. */ public static final String EXTRA_NETWORK_INFO = "networkInfo"; /** * The lookup key for a String giving the BSSID of the access point to which * we are connected. No longer used. */ @Deprecated public static final String EXTRA_BSSID = "bssid"; /** * The lookup key for a {@link android.net.wifi.WifiInfo} object giving the * information about the access point to which we are connected. * No longer used. */ @Deprecated public static final String EXTRA_WIFI_INFO = "wifiInfo"; /** * Broadcast intent action indicating that the state of establishing a connection to * an access point has changed.One extra provides the new * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and * is not generally the most useful thing to look at if you are just interested in * the overall state of connectivity. * @see #EXTRA_NEW_STATE * @see #EXTRA_SUPPLICANT_ERROR * @deprecated This is no longer supported. */ @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE"; /** * The lookup key for a {@link SupplicantState} describing the new state * Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. * @deprecated This is no longer supported. */ @Deprecated public static final String EXTRA_NEW_STATE = "newState"; /** * The lookup key for a {@link SupplicantState} describing the supplicant * error code if any * Retrieve with * {@link android.content.Intent#getIntExtra(String, int)}. * @see #ERROR_AUTHENTICATING * @deprecated This is no longer supported. */ @Deprecated public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError"; /** * The lookup key for a {@link SupplicantState} describing the supplicant * error reason if any * Retrieve with * {@link android.content.Intent#getIntExtra(String, int)}. * @see #ERROR_AUTH_FAILURE_#REASON_CODE * @deprecated This is no longer supported. * @hide */ @Deprecated public static final String EXTRA_SUPPLICANT_ERROR_REASON = "supplicantErrorReason"; /** * Broadcast intent action indicating that the configured networks changed. * This can be as a result of adding/updating/deleting a network. *
* {@link #EXTRA_CHANGE_REASON} contains whether the configuration was added/changed/removed. * {@link #EXTRA_WIFI_CONFIGURATION} is never set beginning in * {@link android.os.Build.VERSION_CODES#R}. * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set for backwards compatibility reasons, but * its value is always true beginning in {@link android.os.Build.VERSION_CODES#R}, even if only * a single network changed. *
* The {@link android.Manifest.permission#ACCESS_WIFI_STATE ACCESS_WIFI_STATE} permission is * required to receive this broadcast. * * @hide */ @SystemApi public static final String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE"; /** * The lookup key for a {@link android.net.wifi.WifiConfiguration} object representing * the changed Wi-Fi configuration when the {@link #CONFIGURED_NETWORKS_CHANGED_ACTION} * broadcast is sent. * @deprecated This extra is never set beginning in {@link android.os.Build.VERSION_CODES#R}, * regardless of the target SDK version. Use {@link #getConfiguredNetworks} to get the full list * of configured networks. * @hide */ @Deprecated @SystemApi public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration"; /** * Multiple network configurations have changed. * @see #CONFIGURED_NETWORKS_CHANGED_ACTION * @deprecated This extra's value is always true beginning in * {@link android.os.Build.VERSION_CODES#R}, regardless of the target SDK version. * @hide */ @Deprecated @SystemApi public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; /** * The lookup key for an integer indicating the reason a Wi-Fi network configuration * has changed. One of {@link #CHANGE_REASON_ADDED}, {@link #CHANGE_REASON_REMOVED}, * {@link #CHANGE_REASON_CONFIG_CHANGE}. * * @see #CONFIGURED_NETWORKS_CHANGED_ACTION * @hide */ @SystemApi public static final String EXTRA_CHANGE_REASON = "changeReason"; /** * The configuration is new and was added. * @hide */ @SystemApi public static final int CHANGE_REASON_ADDED = 0; /** * The configuration was removed and is no longer present in the system's list of * configured networks. * @hide */ @SystemApi public static final int CHANGE_REASON_REMOVED = 1; /** * The configuration has changed as a result of explicit action or because the system * took an automated action such as disabling a malfunctioning configuration. * @hide */ @SystemApi public static final int CHANGE_REASON_CONFIG_CHANGE = 2; /** * An access point scan has completed, and results are available. * Call {@link #getScanResults()} to obtain the results. * The broadcast intent may contain an extra field with the key {@link #EXTRA_RESULTS_UPDATED} * and a {@code boolean} value indicating if the scan was successful. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"; /** * Lookup key for a {@code boolean} extra in intent {@link #SCAN_RESULTS_AVAILABLE_ACTION} * representing if the scan was successful or not. * Scans may fail for multiple reasons, these may include: *

    *
  1. An app requested too many scans in a certain period of time. * This may lead to additional scan request rejections via "scan throttling" for both * foreground and background apps. * Note: Apps holding android.Manifest.permission.NETWORK_SETTINGS permission are * exempted from scan throttling. *
  2. *
  3. The device is idle and scanning is disabled.
  4. *
  5. Wifi hardware reported a scan failure.
  6. *
* @return true scan was successful, results are updated * @return false scan was not successful, results haven't been updated since previous scan */ public static final String EXTRA_RESULTS_UPDATED = "resultsUpdated"; /** * A batch of access point scans has been completed and the results areavailable. * Call {@link #getBatchedScanResults()} to obtain the results. * @deprecated This API is nolonger supported. * Use {@link WifiScanner} API * @hide */ @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String BATCHED_SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.BATCHED_RESULTS"; /** * The RSSI (signal strength) has changed. * * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE * @see #EXTRA_NEW_RSSI */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED"; /** * The lookup key for an {@code int} giving the new RSSI in dBm. */ public static final String EXTRA_NEW_RSSI = "newRssi"; /** * @see #ACTION_LINK_CONFIGURATION_CHANGED * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String LINK_CONFIGURATION_CHANGED_ACTION = "android.net.wifi.LINK_CONFIGURATION_CHANGED"; /** * Broadcast intent action indicating that the link configuration changed on wifi. *
No permissions are required to listen to this broadcast. * @hide */ @SystemApi public static final String ACTION_LINK_CONFIGURATION_CHANGED = // should be android.net.wifi.action.LINK_CONFIGURATION_CHANGED, but due to // @UnsupportedAppUsage leaving it as android.net.wifi.LINK_CONFIGURATION_CHANGED. LINK_CONFIGURATION_CHANGED_ACTION; /** * The lookup key for a {@link android.net.LinkProperties} object associated with the * Wi-Fi network. * Included in the {@link #ACTION_LINK_CONFIGURATION_CHANGED} broadcast. * * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. * * @deprecated this extra is no longer populated. * * @hide */ @Deprecated @SystemApi public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES"; /** * The lookup key for a {@link android.net.NetworkCapabilities} object associated with the * Wi-Fi network. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. * @hide */ public static final String EXTRA_NETWORK_CAPABILITIES = "networkCapabilities"; /** * The network IDs of the configured networks could have changed. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED"; /** * Activity Action: Show a system activity that allows the user to enable * scans to be available even with Wi-Fi turned off. * *

Notification of the result of this activity is posted using the * {@link android.app.Activity#onActivityResult} callback. The * resultCode * will be {@link android.app.Activity#RESULT_OK} if scan always mode has * been turned on or {@link android.app.Activity#RESULT_CANCELED} if the user * has rejected the request or an error has occurred. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; /** * Activity Action: Pick a Wi-Fi network to connect to. *

Input: Nothing. *

Output: Nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; /** * Activity Action: Receiver should show UI to get user approval to enable WiFi. *

Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with * the name of the app requesting the action. *

Output: Nothing. *

No permissions are required to send this action. * @hide */ @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_ENABLE = "android.net.wifi.action.REQUEST_ENABLE"; /** * Activity Action: Receiver should show UI to get user approval to disable WiFi. *

Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with * the name of the app requesting the action. *

Output: Nothing. *

No permissions are required to send this action. * @hide */ @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE"; /** * Directed broadcast intent action indicating that the device has connected to one of the * network suggestions provided by the app. This will be sent post connection to a network * which was created with {@link WifiNetworkSuggestion.Builder#setIsAppInteractionRequired( * boolean)} * flag set. *

* Note: The broadcast is sent to the app only if it holds * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission. * * @see #EXTRA_NETWORK_SUGGESTION */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION"; /** * Sent as as a part of {@link #ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION} that holds * an instance of {@link WifiNetworkSuggestion} corresponding to the connected network. */ public static final String EXTRA_NETWORK_SUGGESTION = "android.net.wifi.extra.NETWORK_SUGGESTION"; /** * Internally used Wi-Fi lock mode representing the case were no locks are held. * @hide */ public static final int WIFI_MODE_NO_LOCKS_HELD = 0; /** * In this Wi-Fi lock mode, Wi-Fi will be kept active, * and will behave normally, i.e., it will attempt to automatically * establish a connection to a remembered access point that is * within range, and will do periodic scans if there are remembered * access points but none are in range. * * @deprecated This API is non-functional and will have no impact. */ @Deprecated public static final int WIFI_MODE_FULL = 1; /** * In this Wi-Fi lock mode, Wi-Fi will be kept active, * but the only operation that will be supported is initiation of * scans, and the subsequent reporting of scan results. No attempts * will be made to automatically connect to remembered access points, * nor will periodic scans be automatically performed looking for * remembered access points. Scans must be explicitly requested by * an application in this mode. * * @deprecated This API is non-functional and will have no impact. */ @Deprecated public static final int WIFI_MODE_SCAN_ONLY = 2; /** * In this Wi-Fi lock mode, Wi-Fi will not go to power save. * This results in operating with low packet latency. * The lock is only active when the device is connected to an access point. * The lock is active even when the device screen is off or the acquiring application is * running in the background. * This mode will consume more power and hence should be used only * when there is a need for this tradeoff. *

* An example use case is when a voice connection needs to be * kept active even after the device screen goes off. * Holding a {@link #WIFI_MODE_FULL_HIGH_PERF} lock for the * duration of the voice call may improve the call quality. *

* When there is no support from the hardware, the {@link #WIFI_MODE_FULL_HIGH_PERF} * lock will have no impact. * * @deprecated The {@code WIFI_MODE_FULL_HIGH_PERF} is deprecated and is automatically replaced * with {@link #WIFI_MODE_FULL_LOW_LATENCY} with all the restrictions documented on that lock. * I.e. any request to the {@code WIFI_MODE_FULL_HIGH_PERF} will now obtain a * {@link #WIFI_MODE_FULL_LOW_LATENCY} lock instead. * Deprecation is due to the impact of {@code WIFI_MODE_FULL_HIGH_PERF} on power dissipation. * The {@link #WIFI_MODE_FULL_LOW_LATENCY} provides much of the same desired functionality with * less impact on power dissipation. */ @Deprecated public static final int WIFI_MODE_FULL_HIGH_PERF = 3; /** * In this Wi-Fi lock mode, Wi-Fi will operate with a priority to achieve low latency. * {@link #WIFI_MODE_FULL_LOW_LATENCY} lock has the following limitations: *

    *
  1. The lock is only active when the device is connected to an access point.
  2. *
  3. The lock is only active when the screen is on.
  4. *
  5. The lock is only active when the acquiring app is running in the foreground.
  6. *
* Low latency mode optimizes for reduced packet latency, * and as a result other performance measures may suffer when there are trade-offs to make: *
    *
  1. Battery life may be reduced.
  2. *
  3. Throughput may be reduced.
  4. *
  5. Frequency of Wi-Fi scanning may be reduced. This may result in:
  6. * *
*

* Example use cases are real time gaming or virtual reality applications where * low latency is a key factor for user experience. *

* Note: For an app which acquires both {@link #WIFI_MODE_FULL_LOW_LATENCY} and * {@link #WIFI_MODE_FULL_HIGH_PERF} locks, {@link #WIFI_MODE_FULL_LOW_LATENCY} * lock will be effective when app is running in foreground and screen is on, * while the {@link #WIFI_MODE_FULL_HIGH_PERF} lock will take effect otherwise. */ public static final int WIFI_MODE_FULL_LOW_LATENCY = 4; /** Anything worse than or equal to this will show 0 bars. */ @UnsupportedAppUsage private static final int MIN_RSSI = -100; /** Anything better than or equal to this will show the max bars. */ @UnsupportedAppUsage private static final int MAX_RSSI = -55; /** * Number of RSSI levels used in the framework to initiate {@link #RSSI_CHANGED_ACTION} * broadcast, where each level corresponds to a range of RSSI values. * The {@link #RSSI_CHANGED_ACTION} broadcast will only fire if the RSSI * change is significant enough to change the RSSI signal level. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int RSSI_LEVELS = 5; //TODO (b/146346676): This needs to be removed, not used in the code. /** * Auto settings in the driver. The driver could choose to operate on both * 2.4 GHz and 5 GHz or make a dynamic decision on selecting the band. * @hide */ @UnsupportedAppUsage public static final int WIFI_FREQUENCY_BAND_AUTO = 0; /** * Operation on 5 GHz alone * @hide */ @UnsupportedAppUsage public static final int WIFI_FREQUENCY_BAND_5GHZ = 1; /** * Operation on 2.4 GHz alone * @hide */ @UnsupportedAppUsage public static final int WIFI_FREQUENCY_BAND_2GHZ = 2; /** @hide */ public static final boolean DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED = false; /** * Maximum number of active locks we allow. * This limit was added to prevent apps from creating a ridiculous number * of locks and crashing the system by overflowing the global ref table. */ private static final int MAX_ACTIVE_LOCKS = 50; /** Indicates an invalid SSID. */ public static final String UNKNOWN_SSID = ""; /** @hide */ public static final MacAddress ALL_ZEROS_MAC_ADDRESS = MacAddress.fromString("00:00:00:00:00:00"); /** @hide */ @IntDef(flag = false, prefix = { "WIFI_MULTI_INTERNET_MODE_" }, value = { WIFI_MULTI_INTERNET_MODE_DISABLED, WIFI_MULTI_INTERNET_MODE_DBS_AP, WIFI_MULTI_INTERNET_MODE_MULTI_AP, }) @Retention(RetentionPolicy.SOURCE) public @interface WifiMultiInternetMode {} /** * Wi-Fi simultaneous connection to multiple internet-providing Wi-Fi networks (APs) is * disabled. * * @see #getStaConcurrencyForMultiInternetMode() * */ public static final int WIFI_MULTI_INTERNET_MODE_DISABLED = 0; /** * Wi-Fi simultaneous connection to multiple internet-providing Wi-FI networks (APs) is enabled * and restricted to a single network on different bands (e.g. a DBS AP). * * @see #getStaConcurrencyForMultiInternetMode() * */ public static final int WIFI_MULTI_INTERNET_MODE_DBS_AP = 1; /** * Wi-Fi simultaneous connection to multiple internet-providing Wi-Fi networks (APs) is enabled. * The device can connect to any networks/APs - it is just restricted to using different bands * for individual connections. * * @see #getStaConcurrencyForMultiInternetMode() * */ public static final int WIFI_MULTI_INTERNET_MODE_MULTI_AP = 2; /** * The bundle key string for the channel frequency in MHz. * See {@link #getChannelData(Executor, Consumer)} */ public static final String CHANNEL_DATA_KEY_FREQUENCY_MHZ = "CHANNEL_DATA_KEY_FREQUENCY_MHZ"; /** * The bundle key for the number of APs found on the corresponding channel specified by * {@link WifiManager#CHANNEL_DATA_KEY_FREQUENCY_MHZ}. * See {@link #getChannelData(Executor, Consumer)} */ public static final String CHANNEL_DATA_KEY_NUM_AP = "CHANNEL_DATA_KEY_NUM_AP"; /** * This policy is being tracked by the Wifi service. * Indicates success for {@link #addQosPolicies(List, Executor, Consumer)}. * @hide */ @SystemApi public static final int QOS_REQUEST_STATUS_TRACKING = 0; /** * A policy with the same policy ID is already being tracked. * @hide */ @SystemApi public static final int QOS_REQUEST_STATUS_ALREADY_ACTIVE = 1; /** * There are insufficient resources to handle this request at this time. * @hide */ @SystemApi public static final int QOS_REQUEST_STATUS_INSUFFICIENT_RESOURCES = 2; /** * The parameters in the policy request are invalid. * @hide */ @SystemApi public static final int QOS_REQUEST_STATUS_INVALID_PARAMETERS = 3; /** * An unspecified failure occurred while processing this request. * @hide */ @SystemApi public static final int QOS_REQUEST_STATUS_FAILURE_UNKNOWN = 4; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"QOS_REQUEST_STATUS_"}, value = { QOS_REQUEST_STATUS_TRACKING, QOS_REQUEST_STATUS_ALREADY_ACTIVE, QOS_REQUEST_STATUS_INSUFFICIENT_RESOURCES, QOS_REQUEST_STATUS_INVALID_PARAMETERS, QOS_REQUEST_STATUS_FAILURE_UNKNOWN}) public @interface QosRequestStatus {} /** * Maximum number of policies that can be included in a QoS add/remove request. */ private static final int MAX_POLICIES_PER_QOS_REQUEST = 16; /** * Get the maximum number of policies that can be included in a request to * {@link #addQosPolicies(List, Executor, Consumer)} or {@link #removeQosPolicies(int[])}. * @hide */ @SystemApi public static int getMaxNumberOfPoliciesPerQosRequest() { return MAX_POLICIES_PER_QOS_REQUEST; } /* Number of currently active WifiLocks and MulticastLocks */ @UnsupportedAppUsage private int mActiveLockCount; private Context mContext; @UnsupportedAppUsage IWifiManager mService; private final int mTargetSdkVersion; private Looper mLooper; private boolean mVerboseLoggingEnabled = false; private final Object mLock = new Object(); // lock guarding access to the following vars @GuardedBy("mLock") private LocalOnlyHotspotCallbackProxy mLOHSCallbackProxy; @GuardedBy("mLock") private LocalOnlyHotspotObserverProxy mLOHSObserverProxy; private static final SparseArray sOnWifiUsabilityStatsListenerMap = new SparseArray(); private static final SparseArray sSuggestionConnectionStatusListenerMap = new SparseArray(); private static final SparseArray sSuggestionUserApprovalStatusListenerMap = new SparseArray(); private static final SparseArray sWifiVerboseLoggingStatusChangedListenerMap = new SparseArray(); private static final SparseArray sNetworkRequestMatchCallbackMap = new SparseArray(); private static final SparseArray sTrafficStateCallbackMap = new SparseArray(); private static final SparseArray sSoftApCallbackMap = new SparseArray(); private static final SparseArray sActiveCountryCodeChangedCallbackMap = new SparseArray(); private static final SparseArray sLocalOnlyHotspotSoftApCallbackMap = new SparseArray(); private static final SparseArray sLocalOnlyConnectionStatusListenerMap = new SparseArray(); private static final SparseArray sOnWifiNetworkStateChangedListenerMap = new SparseArray<>(); private static final SparseArray sWifiLowLatencyLockListenerMap = new SparseArray<>(); /** * Multi-link operation (MLO) will allow Wi-Fi devices to operate on multiple links at the same * time through a single connection, aiming to support applications that require lower latency, * and higher capacity. Chip vendors have algorithms that run on the chip to use available links * based on incoming traffic and various inputs. Below is a list of Multi-Link Operation modes * that applications can suggest to be accommodated in the algorithm. * * The default MLO mode is for chip vendors to use algorithms to select the optimum links to * operate on, without any guidance from the calling app. * * @hide */ @SystemApi public static final int MLO_MODE_DEFAULT = 0; /** * Low latency mode for Multi-link operation. In this mode, the chip vendor's algorithm * should select MLO links that will achieve low latency. * * @hide */ @SystemApi public static final int MLO_MODE_LOW_LATENCY = 1; /** * High throughput mode for Multi-link operation. In this mode, the chip vendor's algorithm * should select MLO links that will achieve higher throughput. * * @hide */ @SystemApi public static final int MLO_MODE_HIGH_THROUGHPUT = 2; /** * Low power mode for Multi-link operation. In this mode, the chip vendor's algorithm * should select MLO links that will achieve low power. * * @hide */ @SystemApi public static final int MLO_MODE_LOW_POWER = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"MLO_MODE_"}, value = { MLO_MODE_DEFAULT, MLO_MODE_LOW_LATENCY, MLO_MODE_HIGH_THROUGHPUT, MLO_MODE_LOW_POWER}) public @interface MloMode { } /** * Roaming is disabled. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int ROAMING_MODE_NONE = 0; /** * Chipset has roaming trigger capability based on the score calculated * using multiple parameters. If device is configured to this mode then it * will be using chipset's normal (default) roaming. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int ROAMING_MODE_NORMAL = 1; /** * Allows the device to roam more quickly than the normal roaming mode. * Used in cases such as where APs are installed in a high density. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int ROAMING_MODE_AGGRESSIVE = 2; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"ROAMING_MODE_"}, value = { ROAMING_MODE_NONE, ROAMING_MODE_NORMAL, ROAMING_MODE_AGGRESSIVE}) public @interface RoamingMode { } /** * Create a new WifiManager instance. * Applications will almost always want to use * {@link android.content.Context#getSystemService Context.getSystemService} to retrieve * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}. * * @param context the application context * @param service the Binder interface * @param looper the Looper used to deliver callbacks * @hide - hide this because it takes in a parameter of type IWifiManager, which * is a system private class. */ public WifiManager(@NonNull Context context, @NonNull IWifiManager service, @NonNull Looper looper) { mContext = context; mService = service; mLooper = looper; mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; updateVerboseLoggingEnabledFromService(); } /** * Return a list of all the networks configured for the current foreground * user. * * Not all fields of WifiConfiguration are returned. Only the following * fields are filled in: *

* @return a list of network configurations in the form of a list * of {@link WifiConfiguration} objects. * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return an * empty list. *

* Deprecation Exemptions: *

*/ @Deprecated @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) public List getConfiguredNetworks() { try { ParceledListSlice parceledList = mService.getConfiguredNetworks(mContext.getOpPackageName(), mContext.getAttributionTag(), false); if (parceledList == null) { return Collections.emptyList(); } return parceledList.getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return a list of all the networks previously configured by the calling app. Can * be called by Device Owner (DO), Profile Owner (PO), Callers with Carrier privilege and * system apps. * * @return a list of network configurations in the form of a list * of {@link WifiConfiguration} objects. * @throws SecurityException if the caller is not allowed to call this API */ @RequiresPermission(ACCESS_WIFI_STATE) @NonNull public List getCallerConfiguredNetworks() { try { ParceledListSlice parceledList = mService.getConfiguredNetworks(mContext.getOpPackageName(), mContext.getAttributionTag(), true); if (parceledList == null) { return Collections.emptyList(); } return parceledList.getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Applications targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later need to have * the following permissions: {@link android.Manifest.permission#NEARBY_WIFI_DEVICES}, * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and * {@link android.Manifest.permission#READ_WIFI_CREDENTIAL}. * Applications targeting {@link Build.VERSION_CODES#S} or prior SDK levels need to have the * following permissions: {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, * {@link android.Manifest.permission#CHANGE_WIFI_STATE} and * {@link android.Manifest.permission#READ_WIFI_CREDENTIAL}. *

See {@link #getPrivilegedConnectedNetwork()} to get the WifiConfiguration for only the * connected network that's providing internet by default. * * @hide **/ @SystemApi @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, NEARBY_WIFI_DEVICES, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL}, conditional = true) public List getPrivilegedConfiguredNetworks() { try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } ParceledListSlice parceledList = mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName(), mContext.getAttributionTag(), extras); if (parceledList == null) { return Collections.emptyList(); } return parceledList.getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Gets the {@link WifiConfiguration} with credentials of the connected wifi network * that's providing internet by default. *

* On {@link android.os.Build.VERSION_CODES#TIRAMISU} or later SDKs, the caller need to have * the following permissions: {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation", * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and * {@link android.Manifest.permission#READ_WIFI_CREDENTIAL}. If the app does not have * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. *

* On {@link Build.VERSION_CODES#S} or prior SDKs, the caller need to have the * following permissions: {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, * {@link android.Manifest.permission#CHANGE_WIFI_STATE} and * {@link android.Manifest.permission#READ_WIFI_CREDENTIAL}. * * @return The WifiConfiguration representation of the connected wifi network providing * internet, or null if wifi is not connected. * * @throws SecurityException if caller does not have the required permissions * @hide **/ @SystemApi @RequiresPermission(allOf = {NEARBY_WIFI_DEVICES, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL}, conditional = true) @Nullable public WifiConfiguration getPrivilegedConnectedNetwork() { try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } return mService.getPrivilegedConnectedNetwork(mContext.getOpPackageName(), mContext.getAttributionTag(), extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a list of all matching WifiConfigurations of PasspointConfiguration for a given list * of ScanResult. * * An empty list will be returned when no PasspointConfiguration are installed or if no * PasspointConfiguration match the ScanResult. * * @param scanResults a list of scanResult that represents the BSSID * @return List that consists of {@link WifiConfiguration} and corresponding scanResults per * network type({@link #PASSPOINT_HOME_NETWORK} and {@link #PASSPOINT_ROAMING_NETWORK}). * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) @NonNull public List>>> getAllMatchingWifiConfigs( @NonNull List scanResults) { List>>> configs = new ArrayList<>(); try { Map>> results = mService.getAllMatchingPasspointProfilesForScanResults(scanResults); if (results.isEmpty()) { return configs; } List wifiConfigurations = mService.getWifiConfigsForPasspointProfiles( new ArrayList<>(results.keySet())); for (WifiConfiguration configuration : wifiConfigurations) { Map> scanResultsPerNetworkType = results.get(configuration.getProfileKey()); if (scanResultsPerNetworkType != null) { configs.add(Pair.create(configuration, scanResultsPerNetworkType)); } } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return configs; } /** * To be used with setScreenOnScanSchedule. * @hide */ @SystemApi public static class ScreenOnScanSchedule { private final Duration mScanInterval; private final int mScanType; /** * Creates a ScreenOnScanSchedule. * @param scanInterval Interval between framework-initiated connectivity scans. * @param scanType One of the {@code WifiScanner.SCAN_TYPE_} values. */ public ScreenOnScanSchedule(@NonNull Duration scanInterval, @WifiAnnotations.ScanType int scanType) { if (scanInterval == null) { throw new IllegalArgumentException("scanInterval can't be null"); } mScanInterval = scanInterval; mScanType = scanType; } /** * Gets the interval between framework-initiated connectivity scans. */ public @NonNull Duration getScanInterval() { return mScanInterval; } /** * Gets the type of scan to be used. One of the {@code WifiScanner.SCAN_TYPE_} values. */ public @WifiAnnotations.ScanType int getScanType() { return mScanType; } } /** * This API allows a privileged app to customize the wifi framework's network selection logic. * To revert to default behavior, call this API with a {@link WifiNetworkSelectionConfig} * created from a default {@link WifiNetworkSelectionConfig.Builder}. * * Use {@link WifiManager#getNetworkSelectionConfig(Executor, Consumer)} to get the current * network selection configuration. *

* @param nsConfig an Object representing the network selection configuration being programmed. * This should be created with a {@link WifiNetworkSelectionConfig.Builder}. * * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws IllegalArgumentException if input is invalid. * @throws SecurityException if the caller does not have permission. * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION }) @SystemApi public void setNetworkSelectionConfig(@NonNull WifiNetworkSelectionConfig nsConfig) { try { if (nsConfig == null) { throw new IllegalArgumentException("nsConfig can not be null"); } mService.setNetworkSelectionConfig(nsConfig); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This API allows a privileged app to retrieve the {@link WifiNetworkSelectionConfig} * currently being used by the network selector. * * Use {@link WifiManager#setNetworkSelectionConfig(WifiNetworkSelectionConfig)} to set a * new network selection configuration. *

* @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return * {@link WifiNetworkSelectionConfig} * * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws SecurityException if the caller does not have permission. * @throws NullPointerException if the caller provided invalid inputs. * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION }) @SystemApi public void getNetworkSelectionConfig(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.getNetworkSelectionConfig( new IWifiNetworkSelectionConfigListener.Stub() { @Override public void onResult(WifiNetworkSelectionConfig value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allows a privileged app to enable/disable whether a confirmation dialog should be displayed * when third-party apps attempt to turn on WiFi. * * Use {@link #isThirdPartyAppEnablingWifiConfirmationDialogEnabled()} to get the * currently configured value. * * Note: Only affects behavior for apps with targetSDK < Q, since third party apps are not * allowed to enable wifi on targetSDK >= Q. * * This overrides the overlay value |config_showConfirmationDialogForThirdPartyAppsEnablingWifi| *

* @param enable true to enable the confirmation dialog, false otherwise * * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws SecurityException if the caller does not have permission. * @hide */ @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) @SystemApi public void setThirdPartyAppEnablingWifiConfirmationDialogEnabled(boolean enable) { try { mService.setThirdPartyAppEnablingWifiConfirmationDialogEnabled(enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check whether the wifi configuration indicates that a confirmation dialog should be displayed * when third-party apps attempt to turn on WiFi. * * Use {@link #setThirdPartyAppEnablingWifiConfirmationDialogEnabled(boolean)} to set this * value. * * Note: This setting only affects behavior for apps with targetSDK < Q, since third party apps * are not allowed to enable wifi on targetSDK >= Q. * *

* @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws SecurityException if the caller does not have permission. * @return true if dialog should be displayed, false otherwise. * @hide */ @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) @SystemApi public boolean isThirdPartyAppEnablingWifiConfirmationDialogEnabled() { try { return mService.isThirdPartyAppEnablingWifiConfirmationDialogEnabled(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allows a privileged app to customize the screen-on scan behavior. When a non-null schedule * is set via this API, it will always get used instead of the scan schedules defined in the * overlay. When a null schedule is set via this API, the wifi subsystem will go back to using * the scan schedules defined in the overlay. Also note, the scan schedule will be truncated * (rounded down) to the nearest whole second. *

* Example usage: * The following call specifies that first scheduled scan should be in 20 seconds using * {@link WifiScanner#SCAN_TYPE_HIGH_ACCURACY}, and all * scheduled scans later should happen every 40 seconds using * {@link WifiScanner#SCAN_TYPE_LOW_POWER}. *

     * List schedule = new ArrayList<>();
     * schedule.add(new ScreenOnScanSchedule(Duration.ofSeconds(20),
     *         WifiScanner.SCAN_TYPE_HIGH_ACCURACY));
     * schedule.add(new ScreenOnScanSchedule(Duration.ofSeconds(40),
     *         WifiScanner.SCAN_TYPE_LOW_POWER));
     * wifiManager.setScreenOnScanSchedule(schedule);
     * 
* @param screenOnScanSchedule defines the screen-on scan schedule and the corresponding * scan type. Set to null to clear any previously set value. * * @throws IllegalStateException if input is invalid * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws SecurityException if the caller does not have permission. * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION }) @SystemApi public void setScreenOnScanSchedule(@Nullable List screenOnScanSchedule) { try { if (screenOnScanSchedule == null) { mService.setScreenOnScanSchedule(null, null); return; } if (screenOnScanSchedule.isEmpty()) { throw new IllegalArgumentException("The input should either be null or a non-empty" + " list"); } int[] scanSchedule = new int[screenOnScanSchedule.size()]; int[] scanType = new int[screenOnScanSchedule.size()]; for (int i = 0; i < screenOnScanSchedule.size(); i++) { scanSchedule[i] = (int) screenOnScanSchedule.get(i).getScanInterval().toSeconds(); scanType[i] = screenOnScanSchedule.get(i).getScanType(); } mService.setScreenOnScanSchedule(scanSchedule, scanType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * The Wi-Fi framework may trigger connectivity scans in response to the screen turning on for * network selection purposes. This API allows a privileged app to set a delay to the next * connectivity scan triggered by the Wi-Fi framework in response to the next screen-on event. * This gives a window for the privileged app to issue their own custom scans to influence Wi-Fi * network selection. The expected usage is the privileged app monitor for the screen turning * off, and then call this API if it believes delaying the next screen-on connectivity scan is * needed. *

* Note that this API will only delay screen-on connectivity scans once. This API will need to * be called again if further screen-on scan delays are needed after it resolves. * @param delayMs defines the time in milliseconds to delay the next screen-on connectivity * scan. Setting this to 0 will remove the delay. * * @throws IllegalStateException if input is invalid * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws SecurityException if the caller does not have permission. * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION }) @SystemApi public void setOneShotScreenOnConnectivityScanDelayMillis(@IntRange(from = 0) int delayMs) { try { mService.setOneShotScreenOnConnectivityScanDelayMillis(delayMs); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Retrieve a list of {@link WifiConfiguration} for available {@link WifiNetworkSuggestion} * matching the given list of {@link ScanResult}. * * An available {@link WifiNetworkSuggestion} must satisfy: *

    *
  • Matching one of the {@link ScanResult} from the given list. *
  • and {@link WifiNetworkSuggestion.Builder#setIsUserAllowedToManuallyConnect(boolean)} set * to true. *
* * @param scanResults a list of scanResult. * @return a list of @link WifiConfiguration} for available {@link WifiNetworkSuggestion} * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) @NonNull public List getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( @NonNull List scanResults) { try { return mService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(scanResults); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Specify a set of SSIDs that will not get disabled internally by the Wi-Fi subsystem when * connection issues occur. To clear the list, call this API with an empty Set. *

* {@link #getSsidsAllowlist()} can be used to check the SSIDs that have been set. * @param ssids - list of WifiSsid that will not get disabled internally * @throws SecurityException if the calling app is not a Device Owner (DO), Profile Owner (PO), * or a privileged app that has one of the permissions required by * this API. * @throws IllegalArgumentException if the input is null. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}, conditional = true) public void setSsidsAllowlist(@NonNull Set ssids) { if (ssids == null) { throw new IllegalArgumentException(TAG + ": ssids can not be null"); } try { mService.setSsidsAllowlist(mContext.getOpPackageName(), new ArrayList<>(ssids)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the Set of SSIDs that will not get disabled internally by the Wi-Fi subsystem when * connection issues occur. * @throws SecurityException if the calling app is not a Device Owner (DO), Profile Owner (PO), * or a privileged app that has one of the permissions required by * this API. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}, conditional = true) public @NonNull Set getSsidsAllowlist() { try { return new ArraySet( mService.getSsidsAllowlist(mContext.getOpPackageName())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a list of unique Hotspot 2.0 OSU (Online Sign-Up) providers associated with a given * list of ScanResult. * * An empty list will be returned if no match is found. * * @param scanResults a list of ScanResult * @return Map that consists {@link OsuProvider} and a list of matching {@link ScanResult} * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) @NonNull public Map> getMatchingOsuProviders( @Nullable List scanResults) { if (scanResults == null) { return new HashMap<>(); } try { return mService.getMatchingOsuProviders(scanResults); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns the matching Passpoint R2 configurations for given OSU (Online Sign-Up) providers. * * Given a list of OSU providers, this only returns OSU providers that already have Passpoint R2 * configurations in the device. * An empty map will be returned when there is no matching Passpoint R2 configuration for the * given OsuProviders. * * @param osuProviders a set of {@link OsuProvider} * @return Map that consists of {@link OsuProvider} and matching {@link PasspointConfiguration}. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) @NonNull public Map getMatchingPasspointConfigsForOsuProviders( @NonNull Set osuProviders) { try { return mService.getMatchingPasspointConfigsForOsuProviders( new ArrayList<>(osuProviders)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Add a new network description to the set of configured networks. * The {@code networkId} field of the supplied configuration object * is ignored. *

* The new network will be marked DISABLED by default. To enable it, * called {@link #enableNetwork}. * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. * If the {@link WifiConfiguration} has an Http Proxy set * the calling app must be System, or be provisioned as the Profile or Device Owner. * @return the ID of the newly created network description. This is used in * other operations to specified the network to be acted upon. * Returns {@code -1} on failure. * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return * {@code -1}. *

* Deprecation Exemptions: *

    *
  • Device Owner (DO), Profile Owner (PO) and system apps. *
*/ @Deprecated public int addNetwork(WifiConfiguration config) { if (config == null) { return -1; } config.networkId = -1; return addOrUpdateNetwork(config); } /** * This is a new version of {@link #addNetwork(WifiConfiguration)} which returns more detailed * failure codes. The usage of this API is limited to Device Owner (DO), Profile Owner (PO), * system app, and privileged apps. *

* Add a new network description to the set of configured networks. The {@code networkId} * field of the supplied configuration object is ignored. The new network will be marked * DISABLED by default. To enable it, call {@link #enableNetwork}. *

* @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. * If the {@link WifiConfiguration} has an Http Proxy set * the calling app must be System, or be provisioned as the Profile or Device Owner. * @return A {@link AddNetworkResult} Object. * @throws SecurityException if the calling app is not a Device Owner (DO), * Profile Owner (PO), system app, or a privileged app that has one of * the permissions required by this API. * @throws IllegalArgumentException if the input configuration is null or if the * security type in input configuration is not supported. */ @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_MANAGED_PROVISIONING }, conditional = true) @NonNull public AddNetworkResult addNetworkPrivileged(@NonNull WifiConfiguration config) { if (config == null) throw new IllegalArgumentException("config cannot be null"); if (config.isSecurityType(WifiInfo.SECURITY_TYPE_DPP) && !isFeatureSupported(WIFI_FEATURE_DPP_AKM)) { throw new IllegalArgumentException("dpp akm is not supported"); } config.networkId = -1; try { return mService.addOrUpdateNetworkPrivileged(config, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Provides the results of a call to {@link #addNetworkPrivileged(WifiConfiguration)} */ public static final class AddNetworkResult implements Parcelable { /** * The operation has completed successfully. */ public static final int STATUS_SUCCESS = 0; /** * The operation has failed due to an unknown reason. */ public static final int STATUS_FAILURE_UNKNOWN = 1; /** * The calling app does not have permission to call this API. */ public static final int STATUS_NO_PERMISSION = 2; /** * Generic failure code for adding a passpoint network. */ public static final int STATUS_ADD_PASSPOINT_FAILURE = 3; /** * Generic failure code for adding a non-passpoint network. */ public static final int STATUS_ADD_WIFI_CONFIG_FAILURE = 4; /** * The network configuration is invalid. */ public static final int STATUS_INVALID_CONFIGURATION = 5; /** * The calling app has no permission to modify the configuration. */ public static final int STATUS_NO_PERMISSION_MODIFY_CONFIG = 6; /** * The calling app has no permission to modify the proxy setting. */ public static final int STATUS_NO_PERMISSION_MODIFY_PROXY_SETTING = 7; /** * The calling app has no permission to modify the MAC randomization setting. */ public static final int STATUS_NO_PERMISSION_MODIFY_MAC_RANDOMIZATION = 8; /** * Internal failure in updating network keys.. */ public static final int STATUS_FAILURE_UPDATE_NETWORK_KEYS = 9; /** * The enterprise network is missing either the root CA or domain name. */ public static final int STATUS_INVALID_CONFIGURATION_ENTERPRISE = 10; /** @hide */ @IntDef(prefix = { "STATUS_" }, value = { STATUS_SUCCESS, STATUS_FAILURE_UNKNOWN, STATUS_NO_PERMISSION, STATUS_ADD_PASSPOINT_FAILURE, STATUS_ADD_WIFI_CONFIG_FAILURE, STATUS_INVALID_CONFIGURATION, STATUS_NO_PERMISSION_MODIFY_CONFIG, STATUS_NO_PERMISSION_MODIFY_PROXY_SETTING, STATUS_NO_PERMISSION_MODIFY_MAC_RANDOMIZATION, STATUS_FAILURE_UPDATE_NETWORK_KEYS, STATUS_INVALID_CONFIGURATION_ENTERPRISE, }) @Retention(RetentionPolicy.SOURCE) public @interface AddNetworkStatusCode {} @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(statusCode); dest.writeInt(networkId); } /** Implement the Parcelable interface */ public static final @android.annotation.NonNull Creator CREATOR = new Creator() { public AddNetworkResult createFromParcel(Parcel in) { return new AddNetworkResult(in.readInt(), in.readInt()); } public AddNetworkResult[] newArray(int size) { return new AddNetworkResult[size]; } }; /** * One of the {@code STATUS_} values. If the operation is successful this field * will be set to {@code STATUS_SUCCESS}. */ public final @AddNetworkStatusCode int statusCode; /** * The identifier of the added network, which could be used in other operations. This field * will be set to {@code -1} if the operation failed. */ public final int networkId; public AddNetworkResult(@AddNetworkStatusCode int statusCode, int networkId) { this.statusCode = statusCode; this.networkId = networkId; } } /** * Update the network description of an existing configured network. * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. It may * be sparse, so that only the items that are being changed * are non-null. The {@code networkId} field * must be set to the ID of the existing network being updated. * If the {@link WifiConfiguration} has an Http Proxy set * the calling app must be System, or be provisioned as the Profile or Device Owner. * @return Returns the {@code networkId} of the supplied * {@code WifiConfiguration} on success. *
* Returns {@code -1} on failure, including when the {@code networkId} * field of the {@code WifiConfiguration} does not refer to an * existing network. * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return * {@code -1}. *

* Deprecation Exemptions: *

    *
  • Device Owner (DO), Profile Owner (PO) and system apps. *
*/ @Deprecated public int updateNetwork(WifiConfiguration config) { if (config == null || config.networkId < 0) { return -1; } return addOrUpdateNetwork(config); } /** * Internal method for doing the RPC that creates a new network description * or updates an existing one. * * @param config The possibly sparse object containing the variables that * are to set or updated in the network description. * @return the ID of the network on success, {@code -1} on failure. */ private int addOrUpdateNetwork(WifiConfiguration config) { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } try { return mService.addOrUpdateNetwork(config, mContext.getOpPackageName(), extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Interface for indicating user selection from the list of networks presented in the * {@link NetworkRequestMatchCallback#onMatch(List)}. * * The platform will implement this callback and pass it along with the * {@link NetworkRequestMatchCallback#onUserSelectionCallbackRegistration( * NetworkRequestUserSelectionCallback)}. The UI component handling * {@link NetworkRequestMatchCallback} will invoke {@link #select(WifiConfiguration)} or * {@link #reject()} to return the user's selection back to the platform via this callback. * @hide */ @SystemApi public interface NetworkRequestUserSelectionCallback { /** * User selected this network to connect to. * @param wifiConfiguration WifiConfiguration object corresponding to the network * user selected. */ @SuppressLint("CallbackMethodName") default void select(@NonNull WifiConfiguration wifiConfiguration) {} /** * User rejected the app's request. */ @SuppressLint("CallbackMethodName") default void reject() {} } /** * Interface for network request callback. Should be implemented by applications and passed when * calling {@link #registerNetworkRequestMatchCallback(Executor, * WifiManager.NetworkRequestMatchCallback)}. * * This is meant to be implemented by a UI component to present the user with a list of networks * matching the app's request. The user is allowed to pick one of these networks to connect to * or reject the request by the app. * @hide */ @SystemApi public interface NetworkRequestMatchCallback { /** * Invoked to register a callback to be invoked to convey user selection. The callback * object passed in this method is to be invoked by the UI component after the service sends * a list of matching scan networks using {@link #onMatch(List)} and user picks a network * from that list. * * @param userSelectionCallback Callback object to send back the user selection. */ default void onUserSelectionCallbackRegistration( @NonNull NetworkRequestUserSelectionCallback userSelectionCallback) {} /** * Invoked when the active network request is aborted, either because *
  • The app released the request, OR
  • *
  • Request was overridden by a new request
  • * This signals the end of processing for the current request and should stop the UI * component. No subsequent calls from the UI component will be handled by the platform. */ default void onAbort() {} /** * Invoked when a network request initiated by an app matches some networks in scan results. * This may be invoked multiple times for a single network request as the platform finds new * matching networks in scan results. * * @param scanResults List of {@link ScanResult} objects corresponding to the networks * matching the request. */ default void onMatch(@NonNull List scanResults) {} /** * Invoked on a successful connection with the network that the user selected * via {@link NetworkRequestUserSelectionCallback}. * * @param wifiConfiguration WifiConfiguration object corresponding to the network that the * user selected. */ default void onUserSelectionConnectSuccess(@NonNull WifiConfiguration wifiConfiguration) {} /** * Invoked on failure to establish connection with the network that the user selected * via {@link NetworkRequestUserSelectionCallback}. * * @param wifiConfiguration WifiConfiguration object corresponding to the network * user selected. */ default void onUserSelectionConnectFailure(@NonNull WifiConfiguration wifiConfiguration) {} } /** * Callback proxy for NetworkRequestUserSelectionCallback objects. * @hide */ private class NetworkRequestUserSelectionCallbackProxy implements NetworkRequestUserSelectionCallback { private final INetworkRequestUserSelectionCallback mCallback; NetworkRequestUserSelectionCallbackProxy( INetworkRequestUserSelectionCallback callback) { mCallback = callback; } @Override public void select(@NonNull WifiConfiguration wifiConfiguration) { if (mVerboseLoggingEnabled) { Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: select " + "wificonfiguration: " + wifiConfiguration); } try { mCallback.select(wifiConfiguration); } catch (RemoteException e) { Log.e(TAG, "Failed to invoke onSelected", e); throw e.rethrowFromSystemServer(); } } @Override public void reject() { if (mVerboseLoggingEnabled) { Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: reject"); } try { mCallback.reject(); } catch (RemoteException e) { Log.e(TAG, "Failed to invoke onRejected", e); throw e.rethrowFromSystemServer(); } } } /** * Callback proxy for NetworkRequestMatchCallback objects. * @hide */ private class NetworkRequestMatchCallbackProxy extends INetworkRequestMatchCallback.Stub { private final Executor mExecutor; private final NetworkRequestMatchCallback mCallback; NetworkRequestMatchCallbackProxy(Executor executor, NetworkRequestMatchCallback callback) { mExecutor = executor; mCallback = callback; } @Override public void onUserSelectionCallbackRegistration( INetworkRequestUserSelectionCallback userSelectionCallback) { if (mVerboseLoggingEnabled) { Log.v(TAG, "NetworkRequestMatchCallbackProxy: " + "onUserSelectionCallbackRegistration callback: " + userSelectionCallback); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onUserSelectionCallbackRegistration( new NetworkRequestUserSelectionCallbackProxy(userSelectionCallback)); }); } @Override public void onAbort() { if (mVerboseLoggingEnabled) { Log.v(TAG, "NetworkRequestMatchCallbackProxy: onAbort"); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onAbort(); }); } @Override public void onMatch(List scanResults) { if (mVerboseLoggingEnabled) { Log.v(TAG, "NetworkRequestMatchCallbackProxy: onMatch scanResults: " + scanResults); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onMatch(scanResults); }); } @Override public void onUserSelectionConnectSuccess(WifiConfiguration wifiConfiguration) { if (mVerboseLoggingEnabled) { Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectSuccess " + " wificonfiguration: " + wifiConfiguration); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onUserSelectionConnectSuccess(wifiConfiguration); }); } @Override public void onUserSelectionConnectFailure(WifiConfiguration wifiConfiguration) { if (mVerboseLoggingEnabled) { Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectFailure" + " wificonfiguration: " + wifiConfiguration); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onUserSelectionConnectFailure(wifiConfiguration); }); } } /** * Registers a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}. * Caller can unregister a previously registered callback using * {@link #unregisterNetworkRequestMatchCallback(NetworkRequestMatchCallback)} *

    * Applications should have the * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers * without the permission will trigger a {@link java.lang.SecurityException}. *

    * * @param executor The Executor on whose thread to execute the callbacks of the {@code callback} * object. * @param callback Callback for network match events to register. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull @CallbackExecutor Executor executor, @NonNull NetworkRequestMatchCallback callback) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerNetworkRequestMatchCallback: callback=" + callback + ", executor=" + executor); try { synchronized (sNetworkRequestMatchCallbackMap) { INetworkRequestMatchCallback.Stub binderCallback = new NetworkRequestMatchCallbackProxy(executor, callback); sNetworkRequestMatchCallbackMap.put(System.identityHashCode(callback), binderCallback); mService.registerNetworkRequestMatchCallback(binderCallback); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Unregisters a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}. *

    * Applications should have the * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers * without the permission will trigger a {@link java.lang.SecurityException}. *

    * * @param callback Callback for network match events to unregister. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback( @NonNull NetworkRequestMatchCallback callback) { if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback); try { synchronized (sNetworkRequestMatchCallbackMap) { int callbackIdentifier = System.identityHashCode(callback); if (!sNetworkRequestMatchCallbackMap.contains(callbackIdentifier)) { Log.w(TAG, "Unknown external callback " + callbackIdentifier); return; } mService.unregisterNetworkRequestMatchCallback( sNetworkRequestMatchCallbackMap.get(callbackIdentifier)); sNetworkRequestMatchCallbackMap.remove(callbackIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Privileged API to revoke all app state from wifi stack (equivalent to operations that the * wifi stack performs to clear state for an app that was uninstalled. * This removes: *

  • All saved networks or passpoint profiles added by the app
  • *
  • All previously approved peer to peer connection to access points initiated by the app * using {@link WifiNetworkSpecifier}
  • *
  • All network suggestions and approvals provided using {@link WifiNetworkSuggestion}
  • *

    * @param targetAppUid UID of the app. * @param targetAppPackageName Package name of the app. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void removeAppState(int targetAppUid, @NonNull String targetAppPackageName) { try { mService.removeAppState(targetAppUid, targetAppPackageName); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Provide a list of network suggestions to the device. See {@link WifiNetworkSuggestion} * for a detailed explanation of the parameters. * When the device decides to connect to one of the provided network suggestions, platform sends * a directed broadcast {@link #ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION} to the app if * the network was created with * {@link WifiNetworkSuggestion.Builder#setIsAppInteractionRequired(boolean)} flag set and the * app holds {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} * permission. *

    * NOTE: *

      *
    • These networks are just a suggestion to the platform. The platform will ultimately * decide on which network the device connects to.
    • *
    • When an app is uninstalled or disabled, all its suggested networks are discarded. * If the device is currently connected to a suggested network which is being removed then the * device will disconnect from that network.
    • *
    • If user reset network settings, all added suggestions will be discarded. Apps can use * {@link #getNetworkSuggestions()} to check if their suggestions are in the device.
    • *
    • In-place modification of existing suggestions are allowed.
    • *
        *
      • If the provided suggestions include any previously provided suggestions by the app, * previous suggestions will be updated.
      • *
      • If one of the provided suggestions marks a previously unmetered suggestion as metered and * the device is currently connected to that suggested network, then the device will disconnect * from that network. The system will immediately re-evaluate all the network candidates * and possibly reconnect back to the same suggestion. This disconnect is to make sure that any * traffic flowing over unmetered networks isn't accidentally continued over a metered network. *
      • *
      • * On {@link android.os.Build.VERSION_CODES#TIRAMISU} or above If one of the provided * suggestions marks a previously trusted suggestion as untrusted and the device is currently * connected to that suggested network, then the device will disconnect from that network. The * system will immediately re-evaluate all the network candidates. This disconnect is to make * sure device will not remain connected to an untrusted network without a related * {@link android.net.NetworkRequest}. *
      • *
      *
    * * @param networkSuggestions List of network suggestions provided by the app. * @return Status code for the operation. One of the STATUS_NETWORK_SUGGESTIONS_ values. * @throws SecurityException if the caller is missing required permissions. * @see WifiNetworkSuggestion#equals(Object) */ @RequiresPermission(CHANGE_WIFI_STATE) public @NetworkSuggestionsStatusCode int addNetworkSuggestions( @NonNull List networkSuggestions) { try { return mService.addNetworkSuggestions( networkSuggestions, mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove some or all of the network suggestions that were previously provided by the app. * If one of the suggestions being removed was used to establish connection to the current * network, then the device will immediately disconnect from that network. This method is same * as {@link #removeNetworkSuggestions(List, int)} with * {@link #ACTION_REMOVE_SUGGESTION_DISCONNECT} * * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters. * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used. *

    networkSuggestions) { return removeNetworkSuggestions(networkSuggestions, ACTION_REMOVE_SUGGESTION_DISCONNECT); } /** * Remove some or all of the network suggestions that were previously provided by the app. * If one of the suggestions being removed was used to establish connection to the current * network, then the specified action will be executed. * * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters. * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used. * * @param networkSuggestions List of network suggestions to be removed. Pass an empty list * to remove all the previous suggestions provided by the app. * @param action Desired action to execute after removing the suggestion. One of * {@code ACTION_REMOVE_SUGGESTION_*} * @return Status code for the operation. One of the {@code STATUS_NETWORK_SUGGESTIONS_*} * values. Any matching suggestions are removed from the device and will not be considered for * further connection attempts. */ @RequiresPermission(CHANGE_WIFI_STATE) public @NetworkSuggestionsStatusCode int removeNetworkSuggestions( @NonNull List networkSuggestions, @ActionAfterRemovingSuggestion int action) { try { return mService.removeNetworkSuggestions(networkSuggestions, mContext.getOpPackageName(), action); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get all network suggestions provided by the calling app. * See {@link #addNetworkSuggestions(List)} * See {@link #removeNetworkSuggestions(List)} * @return a list of {@link WifiNetworkSuggestion} */ @RequiresPermission(ACCESS_WIFI_STATE) public @NonNull List getNetworkSuggestions() { try { return mService.getNetworkSuggestions(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Returns the max number of network suggestions that are allowed per app on the device. * @see #addNetworkSuggestions(List) * @see #removeNetworkSuggestions(List) */ public int getMaxNumberOfNetworkSuggestionsPerApp() { return getMaxNumberOfNetworkSuggestionsPerApp( mContext.getSystemService(ActivityManager.class).isLowRamDevice()); } /** @hide */ public static int getMaxNumberOfNetworkSuggestionsPerApp(boolean isLowRamDevice) { return isLowRamDevice ? NETWORK_SUGGESTIONS_MAX_PER_APP_LOW_RAM : NETWORK_SUGGESTIONS_MAX_PER_APP_HIGH_RAM; } /** * Add or update a Passpoint configuration. The configuration provides a credential * for connecting to Passpoint networks that are operated by the Passpoint * service provider specified in the configuration. * * Each configuration is uniquely identified by a unique key which depends on the contents of * the configuration. This allows the caller to install multiple profiles with the same FQDN * (Fully qualified domain name). Therefore, in order to update an existing profile, it is * first required to remove it using {@link WifiManager#removePasspointConfiguration(String)}. * Otherwise, a new profile will be added with both configuration. * * Deprecated for general app usage - except DO/PO apps. * See {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} to * create a passpoint suggestion. * See {@link #addNetworkSuggestions(List)}, {@link #removeNetworkSuggestions(List)} for new * API to add Wi-Fi networks for consideration when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#R} or above, this API will always fail and throw * {@link IllegalArgumentException}. *

    * Deprecation Exemptions: *

      *
    • Device Owner (DO), Profile Owner (PO) and system apps. *
    * * @param config The Passpoint configuration to be added * @throws IllegalArgumentException if configuration is invalid or Passpoint is not enabled on * the device. */ public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) { try { if (!mService.addOrUpdatePasspointConfiguration(config, mContext.getOpPackageName())) { throw new IllegalArgumentException(); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name) added * by the caller. * * @param fqdn The FQDN of the Passpoint configuration added by the caller to be removed * @throws IllegalArgumentException if no configuration is associated with the given FQDN or * Passpoint is not enabled on the device. * @deprecated This will be non-functional in a future release. *
    * Requires {@code android.Manifest.permission.NETWORK_SETTINGS} or * {@code android.Manifest.permission.NETWORK_CARRIER_PROVISIONING}. */ @Deprecated public void removePasspointConfiguration(String fqdn) { try { if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) { throw new IllegalArgumentException(); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return the list of installed Passpoint configurations added by the caller. * * An empty list will be returned when no configurations are installed. * * @return A list of {@link PasspointConfiguration} added by the caller * @deprecated This will be non-functional in a future release. *
    * Requires {@code android.Manifest.permission.NETWORK_SETTINGS} or * {@code android.Manifest.permission.NETWORK_SETUP_WIZARD}. */ @Deprecated public List getPasspointConfigurations() { try { return mService.getPasspointConfigurations(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Query for a Hotspot 2.0 release 2 OSU icon file. An {@link #ACTION_PASSPOINT_ICON} intent * will be broadcasted once the request is completed. The presence of the intent extra * {@link #EXTRA_ICON} will indicate the result of the request. * A missing intent extra {@link #EXTRA_ICON} will indicate a failure. * * @param bssid The BSSID of the AP * @param fileName Name of the icon file (remote file) to query from the AP * * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ public void queryPasspointIcon(long bssid, String fileName) { try { mService.queryPasspointIcon(bssid, fileName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Match the currently associated network against the SP matching the given FQDN * @param fqdn FQDN of the SP * @return ordinal [HomeProvider, RoamingProvider, Incomplete, None, Declined] * @hide */ public int matchProviderWithCurrentNetwork(String fqdn) { try { return mService.matchProviderWithCurrentNetwork(fqdn); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove the specified network from the list of configured networks. * This may result in the asynchronous delivery of state change * events. * * Applications are not allowed to remove networks created by other * applications. * * @param netId the ID of the network as returned by {@link #addNetwork} or {@link * #getConfiguredNetworks}. * @return {@code true} if the operation succeeded * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return * {@code false}. *

    * Deprecation Exemptions: *

      *
    • Device Owner (DO), Profile Owner (PO) and system apps. *
    */ @Deprecated public boolean removeNetwork(int netId) { try { return mService.removeNetwork(netId, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove all configured networks that were not created by the calling app. Can only * be called by a Device Owner (DO) app. * * @return {@code true} if at least one network is removed, {@code false} otherwise * @throws SecurityException if the caller is not a Device Owner app */ @RequiresPermission(CHANGE_WIFI_STATE) public boolean removeNonCallerConfiguredNetworks() { try { return mService.removeNonCallerConfiguredNetworks(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow a previously configured network to be associated with. If * attemptConnect is true, an attempt to connect to the selected * network is initiated. This may result in the asynchronous delivery * of state change events. *

    * Note: Network communication may not use Wi-Fi even if Wi-Fi is connected; * traffic may instead be sent through another network, such as cellular data, * Bluetooth tethering, or Ethernet. For example, traffic will never use a * Wi-Fi network that does not provide Internet access (e.g. a wireless * printer), if another network that does offer Internet access (e.g. * cellular data) is available. Applications that need to ensure that their * network traffic uses Wi-Fi should use APIs such as * {@link Network#bindSocket(java.net.Socket)}, * {@link Network#openConnection(java.net.URL)}, or * {@link ConnectivityManager#bindProcessToNetwork} to do so. * * Applications are not allowed to enable networks created by other * applications. * * @param netId the ID of the network as returned by {@link #addNetwork} or {@link * #getConfiguredNetworks}. * @param attemptConnect The way to select a particular network to connect to is specify * {@code true} for this parameter. * @return {@code true} if the operation succeeded * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return * {@code false}. * Deprecation Exemptions: *

      *
    • Device Owner (DO), Profile Owner (PO) and system apps. *
    */ @Deprecated public boolean enableNetwork(int netId, boolean attemptConnect) { try { return mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Disable a configured network. The specified network will not be * a candidate for associating. This may result in the asynchronous * delivery of state change events. * * Applications are not allowed to disable networks created by other * applications. * * @param netId the ID of the network as returned by {@link #addNetwork} or {@link * #getConfiguredNetworks}. * @return {@code true} if the operation succeeded * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return * {@code false}. *

    * Deprecation Exemptions: *

      *
    • Device Owner (DO), Profile Owner (PO) and system apps. *
    */ @Deprecated public boolean disableNetwork(int netId) { try { return mService.disableNetwork(netId, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Disassociate from the currently active access point. This may result * in the asynchronous delivery of state change events. * @return {@code true} if the operation succeeded * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return * {@code false}. *

    * Deprecation Exemptions: *

      *
    • Device Owner (DO), Profile Owner (PO) and system apps. *
    */ @Deprecated public boolean disconnect() { try { return mService.disconnect(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Reconnect to the currently active access point, if we are currently * disconnected. This may result in the asynchronous delivery of state * change events. * @return {@code true} if the operation succeeded * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return * {@code false}. *

    * Deprecation Exemptions: *

      *
    • Device Owner (DO), Profile Owner (PO) and system apps. *
    */ @Deprecated public boolean reconnect() { try { return mService.reconnect(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Reconnect to the currently active access point, even if we are already * connected. This may result in the asynchronous delivery of state * change events. * @return {@code true} if the operation succeeded * * @deprecated * a) See {@link WifiNetworkSpecifier.Builder#build()} for new * mechanism to trigger connection to a Wi-Fi network. * b) See {@link #addNetworkSuggestions(List)}, * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration * when auto-connecting to wifi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false. */ @Deprecated public boolean reassociate() { try { return mService.reassociate(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check that the supplicant daemon is responding to requests. * @return {@code true} if we were able to communicate with the supplicant and * it returned the expected response to the PING message. * @deprecated Will return the output of {@link #isWifiEnabled()} instead. */ @Deprecated public boolean pingSupplicant() { return isWifiEnabled(); } /** @hide */ public static final long WIFI_FEATURE_INFRA = 1L << 0; // Basic infrastructure mode /** @hide */ public static final long WIFI_FEATURE_PASSPOINT = 1L << 2; // Support for GAS/ANQP /** @hide */ public static final long WIFI_FEATURE_P2P = 1L << 3; // Wifi-Direct /** @hide */ public static final long WIFI_FEATURE_MOBILE_HOTSPOT = 1L << 4; // Soft AP /** @hide */ public static final long WIFI_FEATURE_SCANNER = 1L << 5; // WifiScanner APIs /** @hide */ public static final long WIFI_FEATURE_AWARE = 1L << 6; // Wi-Fi Aware networking /** @hide */ public static final long WIFI_FEATURE_D2D_RTT = 1L << 7; // Device-to-device RTT /** @hide */ public static final long WIFI_FEATURE_D2AP_RTT = 1L << 8; // Device-to-AP RTT /** @hide */ public static final long WIFI_FEATURE_PNO = 1L << 10; // Preferred network offload /** @hide */ public static final long WIFI_FEATURE_TDLS = 1L << 12; // Tunnel directed link setup /** @hide */ public static final long WIFI_FEATURE_TDLS_OFFCHANNEL = 1L << 13; // TDLS off channel /** @hide */ public static final long WIFI_FEATURE_AP_STA = 1L << 15; // AP STA Concurrency /** @hide */ public static final long WIFI_FEATURE_LINK_LAYER_STATS = 1L << 16; // Link layer stats /** @hide */ public static final long WIFI_FEATURE_LOGGER = 1L << 17; // WiFi Logger /** @hide */ public static final long WIFI_FEATURE_RSSI_MONITOR = 1L << 19; // RSSI Monitor /** @hide */ public static final long WIFI_FEATURE_MKEEP_ALIVE = 1L << 20; // mkeep_alive /** @hide */ public static final long WIFI_FEATURE_CONFIG_NDO = 1L << 21; // ND offload /** @hide */ public static final long WIFI_FEATURE_CONTROL_ROAMING = 1L << 23; // Control firmware roaming /** @hide */ public static final long WIFI_FEATURE_IE_WHITELIST = 1L << 24; // Probe IE white listing /** @hide */ public static final long WIFI_FEATURE_SCAN_RAND = 1L << 25; // Random MAC & Probe seq /** @hide */ public static final long WIFI_FEATURE_TX_POWER_LIMIT = 1L << 26; // Set Tx power limit /** @hide */ public static final long WIFI_FEATURE_WPA3_SAE = 1L << 27; // WPA3-Personal SAE /** @hide */ public static final long WIFI_FEATURE_WPA3_SUITE_B = 1L << 28; // WPA3-Enterprise Suite-B /** @hide */ public static final long WIFI_FEATURE_OWE = 1L << 29; // Enhanced Open /** @hide */ public static final long WIFI_FEATURE_LOW_LATENCY = 1L << 30; // Low Latency modes /** @hide */ public static final long WIFI_FEATURE_DPP = 1L << 31; // DPP (Easy-Connect) /** @hide */ public static final long WIFI_FEATURE_P2P_RAND_MAC = 1L << 32; // Random P2P MAC /** @hide */ public static final long WIFI_FEATURE_CONNECTED_RAND_MAC = 1L << 33; // Random STA MAC /** @hide */ public static final long WIFI_FEATURE_AP_RAND_MAC = 1L << 34; // Random AP MAC /** @hide */ public static final long WIFI_FEATURE_MBO = 1L << 35; // MBO Support /** @hide */ public static final long WIFI_FEATURE_OCE = 1L << 36; // OCE Support /** @hide */ public static final long WIFI_FEATURE_WAPI = 1L << 37; // WAPI /** @hide */ public static final long WIFI_FEATURE_FILS_SHA256 = 1L << 38; // FILS-SHA256 /** @hide */ public static final long WIFI_FEATURE_FILS_SHA384 = 1L << 39; // FILS-SHA384 /** @hide */ public static final long WIFI_FEATURE_SAE_PK = 1L << 40; // SAE-PK /** @hide */ public static final long WIFI_FEATURE_STA_BRIDGED_AP = 1L << 41; // STA + Bridged AP /** @hide */ public static final long WIFI_FEATURE_BRIDGED_AP = 1L << 42; // Bridged AP /** @hide */ public static final long WIFI_FEATURE_INFRA_60G = 1L << 43; // 60 GHz Band Support /** * Support for 2 STA's for the local-only (peer to peer) connection + internet connection * concurrency. * @hide */ public static final long WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY = 1L << 44; /** * Support for 2 STA's for the make before break concurrency. * @hide */ public static final long WIFI_FEATURE_ADDITIONAL_STA_MBB = 1L << 45; /** * Support for 2 STA's for the restricted connection + internet connection concurrency. * @hide */ public static final long WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED = 1L << 46; /** * DPP (Easy-Connect) Enrollee Responder mode support * @hide */ public static final long WIFI_FEATURE_DPP_ENROLLEE_RESPONDER = 1L << 47; /** * Passpoint Terms and Conditions feature support * @hide */ public static final long WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS = 1L << 48; /** @hide */ public static final long WIFI_FEATURE_SAE_H2E = 1L << 49; // Hash-to-Element /** @hide */ public static final long WIFI_FEATURE_WFD_R2 = 1L << 50; // Wi-Fi Display R2 /** * RFC 7542 decorated identity support * @hide */ public static final long WIFI_FEATURE_DECORATED_IDENTITY = 1L << 51; /** * Trust On First Use support for WPA Enterprise network * @hide */ public static final long WIFI_FEATURE_TRUST_ON_FIRST_USE = 1L << 52; /** * Support for 2 STA's multi internet concurrency. * @hide */ public static final long WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET = 1L << 53; /** * Support for DPP (Easy-Connect) AKM. * @hide */ public static final long WIFI_FEATURE_DPP_AKM = 1L << 54; /** * Support for setting TLS minimum version. * @hide */ public static final long WIFI_FEATURE_SET_TLS_MINIMUM_VERSION = 1L << 55; /** * Support for TLS v.13. * @hide */ public static final long WIFI_FEATURE_TLS_V1_3 = 1L << 56; /** * Support for Dual Band Simultaneous (DBS) operation. * @hide */ public static final long WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS = 1L << 57; /** * Support for TID-To-Link Mapping negotiation. * @hide */ public static final long WIFI_FEATURE_T2LM_NEGOTIATION = 1L << 58; /** * Support for WEP Wi-Fi Network * @hide */ public static final long WIFI_FEATURE_WEP = 1L << 59; /** * Support for WPA PERSONAL Wi-Fi Network * @hide */ public static final long WIFI_FEATURE_WPA_PERSONAL = 1L << 60; /** * Support for Roaming Mode * @hide */ public static final long WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT = 1L << 61; /** * Supports device-to-device connections when infra STA is disabled. * @hide */ public static final long WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED = 1L << 62; private long getSupportedFeatures() { try { return mService.getSupportedFeatures(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private boolean isFeatureSupported(long feature) { return (getSupportedFeatures() & feature) == feature; } /** * @return true if this adapter supports Passpoint * @hide */ public boolean isPasspointSupported() { return isFeatureSupported(WIFI_FEATURE_PASSPOINT); } /** * @return true if this adapter supports WifiP2pManager (Wi-Fi Direct) */ public boolean isP2pSupported() { return isFeatureSupported(WIFI_FEATURE_P2P); } /** * @return true if this adapter supports portable Wi-Fi hotspot * @hide */ @SystemApi public boolean isPortableHotspotSupported() { return isFeatureSupported(WIFI_FEATURE_MOBILE_HOTSPOT); } /** * @return true if this adapter supports WifiScanner APIs * @hide */ @SystemApi public boolean isWifiScannerSupported() { return isFeatureSupported(WIFI_FEATURE_SCANNER); } /** * @return true if this adapter supports Neighbour Awareness Network APIs * @hide */ public boolean isWifiAwareSupported() { return isFeatureSupported(WIFI_FEATURE_AWARE); } /** * Query whether or not the device supports Station (STA) + Access point (AP) concurrency. * * @return true if this device supports STA + AP concurrency, false otherwise. */ public boolean isStaApConcurrencySupported() { return isFeatureSupported(WIFI_FEATURE_AP_STA); } /** * Query whether or not the device supports concurrent station (STA) connections for local-only * connections using {@link WifiNetworkSpecifier}. * * @return true if this device supports multiple STA concurrency for this use-case, false * otherwise. */ public boolean isStaConcurrencyForLocalOnlyConnectionsSupported() { return isFeatureSupported(WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY); } /** * Query whether or not the device supports concurrent station (STA) connections for * make-before-break wifi to wifi switching. * * Note: This is an internal feature which is not available to apps. * * @return true if this device supports multiple STA concurrency for this use-case, false * otherwise. */ public boolean isMakeBeforeBreakWifiSwitchingSupported() { return isFeatureSupported(WIFI_FEATURE_ADDITIONAL_STA_MBB); } /** * Query whether or not the device supports concurrent station (STA) connections for multi * internet connections. * * @return true if this device supports multiple STA concurrency for this use-case, false * otherwise. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public boolean isStaConcurrencyForMultiInternetSupported() { return isFeatureSupported(WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET); } /** * Query whether or not the device supports concurrent station (STA) connections for restricted * connections using {@link WifiNetworkSuggestion.Builder#setOemPaid(boolean)} / * {@link WifiNetworkSuggestion.Builder#setOemPrivate(boolean)}. * * @return true if this device supports multiple STA concurrency for this use-case, false * otherwise. * @hide */ @SystemApi public boolean isStaConcurrencyForRestrictedConnectionsSupported() { return isFeatureSupported(WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED); } /** * @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)} * with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT} and * {@link android.content.pm.PackageManager#FEATURE_WIFI_AWARE}. * * @return true if this adapter supports Device-to-device RTT * @hide */ @Deprecated @SystemApi public boolean isDeviceToDeviceRttSupported() { return isFeatureSupported(WIFI_FEATURE_D2D_RTT); } /** * @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)} * with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT}. * * @return true if this adapter supports Device-to-AP RTT */ @Deprecated public boolean isDeviceToApRttSupported() { return isFeatureSupported(WIFI_FEATURE_D2AP_RTT); } /** * @return true if this adapter supports offloaded connectivity scan */ public boolean isPreferredNetworkOffloadSupported() { try { return mService.isPnoSupported(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * @return true if this adapter supports Tunnel Directed Link Setup */ public boolean isTdlsSupported() { return isFeatureSupported(WIFI_FEATURE_TDLS); } /** * @return true if this adapter supports Off Channel Tunnel Directed Link Setup * @hide */ public boolean isOffChannelTdlsSupported() { return isFeatureSupported(WIFI_FEATURE_TDLS_OFFCHANNEL); } /** * @return true if this adapter supports advanced power/performance counters */ public boolean isEnhancedPowerReportingSupported() { return isFeatureSupported(WIFI_FEATURE_LINK_LAYER_STATS); } /** * @return true if this device supports connected MAC randomization. * @hide */ @SystemApi public boolean isConnectedMacRandomizationSupported() { return isFeatureSupported(WIFI_FEATURE_CONNECTED_RAND_MAC); } /** * @return true if this device supports AP MAC randomization. * @hide */ @SystemApi public boolean isApMacRandomizationSupported() { return isFeatureSupported(WIFI_FEATURE_AP_RAND_MAC); } /** * @return true if this device supports Low latency mode. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public boolean isLowLatencyModeSupported() { return isFeatureSupported(WIFI_FEATURE_LOW_LATENCY); } /** * Check if the chipset supports 2.4GHz band. * @return {@code true} if supported, {@code false} otherwise. */ public boolean is24GHzBandSupported() { try { return mService.is24GHzBandSupported(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if the chipset supports 5GHz band. * @return {@code true} if supported, {@code false} otherwise. */ public boolean is5GHzBandSupported() { try { return mService.is5GHzBandSupported(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if the chipset supports the 60GHz frequency band. * * @return {@code true} if supported, {@code false} otherwise. */ @RequiresApi(Build.VERSION_CODES.S) public boolean is60GHzBandSupported() { try { return mService.is60GHzBandSupported(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if the chipset supports 6GHz band. * @return {@code true} if supported, {@code false} otherwise. */ public boolean is6GHzBandSupported() { try { return mService.is6GHzBandSupported(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if the chipset supports a certain Wi-Fi standard. * @param standard the IEEE 802.11 standard to check on. * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} * @return {@code true} if supported, {@code false} otherwise. */ public boolean isWifiStandardSupported(@WifiAnnotations.WifiStandard int standard) { try { return mService.isWifiStandardSupported(standard); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Query whether or not the device supports concurrency of Station (STA) + multiple access * points (AP) (where the APs bridged together). * * @return true if this device supports concurrency of STA + multiple APs which are bridged * together, false otherwise. */ public boolean isStaBridgedApConcurrencySupported() { return isFeatureSupported(WIFI_FEATURE_STA_BRIDGED_AP); } /** * Query whether or not the device supports multiple Access point (AP) which are bridged * together. * * @return true if this device supports concurrency of multiple AP which bridged together, * false otherwise. */ public boolean isBridgedApConcurrencySupported() { return isFeatureSupported(WIFI_FEATURE_BRIDGED_AP); } /** * @return true if this devices supports device-to-device (D2d) Wi-Fi use-cases * such as Wi-Fi Direct when infra station (STA) is disabled. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public boolean isD2dSupportedWhenInfraStaDisabled() { return isFeatureSupported(WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED); } /** * Interface for Wi-Fi activity energy info listener. Should be implemented by applications and * set when calling {@link WifiManager#getWifiActivityEnergyInfoAsync}. * * @hide */ @SystemApi public interface OnWifiActivityEnergyInfoListener { /** * Called when Wi-Fi activity energy info is available. * Note: this listener is triggered at most once for each call to * {@link #getWifiActivityEnergyInfoAsync}. * * @param info the latest {@link WifiActivityEnergyInfo}, or null if unavailable. */ void onWifiActivityEnergyInfo(@Nullable WifiActivityEnergyInfo info); } private static class OnWifiActivityEnergyInfoProxy extends IOnWifiActivityEnergyInfoListener.Stub { private final Object mLock = new Object(); @Nullable @GuardedBy("mLock") private Executor mExecutor; @Nullable @GuardedBy("mLock") private OnWifiActivityEnergyInfoListener mListener; OnWifiActivityEnergyInfoProxy(Executor executor, OnWifiActivityEnergyInfoListener listener) { mExecutor = executor; mListener = listener; } @Override public void onWifiActivityEnergyInfo(WifiActivityEnergyInfo info) { Executor executor; OnWifiActivityEnergyInfoListener listener; synchronized (mLock) { if (mExecutor == null || mListener == null) { return; } executor = mExecutor; listener = mListener; // null out to allow garbage collection, prevent triggering listener more than once mExecutor = null; mListener = null; } Binder.clearCallingIdentity(); executor.execute(() -> listener.onWifiActivityEnergyInfo(info)); } } /** * Request to get the current {@link WifiActivityEnergyInfo} asynchronously. * Note: This method will return null if {@link #isEnhancedPowerReportingSupported()} returns * false. * * @param executor the executor that the listener will be invoked on * @param listener the listener that will receive the {@link WifiActivityEnergyInfo} object * when it becomes available. The listener will be triggered at most once for * each call to this method. * * @hide */ @SystemApi @RequiresPermission(ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync( @NonNull @CallbackExecutor Executor executor, @NonNull OnWifiActivityEnergyInfoListener listener) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(listener, "listener cannot be null"); try { mService.getWifiActivityEnergyInfoAsync( new OnWifiActivityEnergyInfoProxy(executor, listener)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Request a scan for access points. Returns immediately. The availability * of the results is made known later by means of an asynchronous event sent * on completion of the scan. *

    * To initiate a Wi-Fi scan, declare the * {@link android.Manifest.permission#CHANGE_WIFI_STATE} * permission in the manifest, and perform these steps: *

    *
      *
    1. Invoke the following method: * {@code ((WifiManager) getSystemService(WIFI_SERVICE)).startScan()}
    2. *
    3. * Register a BroadcastReceiver to listen to * {@code SCAN_RESULTS_AVAILABLE_ACTION}.
    4. *
    5. When a broadcast is received, call: * {@code ((WifiManager) getSystemService(WIFI_SERVICE)).getScanResults()}
    6. *
    * @return {@code true} if the operation succeeded, i.e., the scan was initiated. * @deprecated The ability for apps to trigger scan requests will be removed in a future * release. */ @Deprecated public boolean startScan() { return startScan(null); } /** @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(WorkSource workSource) { try { String packageName = mContext.getOpPackageName(); String attributionTag = mContext.getAttributionTag(); return mService.startScan(packageName, attributionTag); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * WPS has been deprecated from Client mode operation. * * @return null * @hide * @deprecated This API is deprecated */ public String getCurrentNetworkWpsNfcConfigurationToken() { return null; } /** * Return dynamic information about the current Wi-Fi connection, if any is active. *

    * * @return the Wi-Fi information, contained in {@link WifiInfo}. * * @deprecated Starting with {@link Build.VERSION_CODES#S}, WifiInfo retrieval is moved to * {@link ConnectivityManager} API surface. WifiInfo is attached in * {@link NetworkCapabilities#getTransportInfo()} which is available via callback in * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} or on-demand from * {@link ConnectivityManager#getNetworkCapabilities(Network)}. * *

    * Usage example: *
         * final NetworkRequest request =
         *      new NetworkRequest.Builder()
         *      .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
         *      .build();
         * final ConnectivityManager connectivityManager =
         *      context.getSystemService(ConnectivityManager.class);
         * final NetworkCallback networkCallback = new NetworkCallback() {
         *      ...
         *      @Override
         *      void onAvailable(Network network) {}
         *
         *      @Override
         *      void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
         *          WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
         *      }
         *      // etc.
         * };
         * connectivityManager.requestNetwork(request, networkCallback); // For request
         * connectivityManager.registerNetworkCallback(request, networkCallback); // For listen
         * 
    *

    * Compatibility Notes: *

  • Apps can continue using this API, however newer features * such as ability to mask out location sensitive data in WifiInfo will not be supported * via this API.
  • *
  • On devices supporting concurrent connections (indicated via * {@link #isStaConcurrencyForLocalOnlyConnectionsSupported()}, etc) this API will return * the details of the internet providing connection (if any) to all apps, except for the apps * that triggered the creation of the concurrent connection. For such apps, this API will return * the details of the connection they created. e.g. apps using {@link WifiNetworkSpecifier} will * trigger a concurrent connection on supported devices and hence this API will provide * details of their peer to peer connection (not the internet providing connection). This * is to maintain backwards compatibility with behavior on single STA devices.
  • *

    */ @Deprecated @RequiresPermission(allOf = {ACCESS_WIFI_STATE, ACCESS_FINE_LOCATION}, conditional = true) public WifiInfo getConnectionInfo() { try { return mService.getConnectionInfo(mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return the results of the latest access point scan. * @return the list of access points found in the most recent scan. An app must hold * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission * and {@link android.Manifest.permission#ACCESS_WIFI_STATE} permission * in order to get valid results. * *

    * When an Access Point’s beacon or probe response includes a Multi-BSSID Element, the * returned scan results should include separate scan result for each BSSID within the * Multi-BSSID Information Element. This includes both transmitted and non-transmitted BSSIDs. * Original Multi-BSSID Element will be included in the Information Elements attached to * each of the scan results. * Note: This is the expected behavior for devices supporting 11ax (WiFi-6) and above, and an * optional requirement for devices running with older WiFi generations. *

    */ @RequiresPermission(allOf = {ACCESS_WIFI_STATE, ACCESS_FINE_LOCATION}) public List getScanResults() { try { ParceledListSlice parceledList = mService .getScanResults(mContext.getOpPackageName(), mContext.getAttributionTag()); if (parceledList == null) { return Collections.emptyList(); } return parceledList.getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the filtered ScanResults which match the network configurations specified by the * {@code networkSuggestionsToMatch}. Suggestions which use {@link WifiConfiguration} use * SSID and the security type to match. Suggestions which use {@link PasspointConfigration} * use the matching rules of Hotspot 2.0. * @param networkSuggestionsToMatch The list of {@link WifiNetworkSuggestion} to match against. * These may or may not be suggestions which are installed on the device. * @param scanResults The scan results to be filtered. Optional - if not provided(empty list), * the Wi-Fi service will use the most recent scan results which the system has. * @return The map of {@link WifiNetworkSuggestion} to the list of {@link ScanResult} * corresponding to networks which match them. * @hide */ @SystemApi @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) @NonNull public Map> getMatchingScanResults( @NonNull List networkSuggestionsToMatch, @Nullable List scanResults) { if (networkSuggestionsToMatch == null) { throw new IllegalArgumentException("networkSuggestions must not be null."); } try { return mService.getMatchingScanResults( networkSuggestionsToMatch, scanResults, mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Set if scanning is always available. * * If set to {@code true}, apps can issue {@link #startScan} and fetch scan results * even when Wi-Fi is turned off. * * @param isAvailable true to enable, false to disable. * @hide * @see #isScanAlwaysAvailable() */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean isAvailable) { try { mService.setScanAlwaysAvailable(isAvailable, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if scanning is always available. * * If this return {@code true}, apps can issue {@link #startScan} and fetch scan results * even when Wi-Fi is turned off. * * To change this setting, see {@link #ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE}. * @deprecated The ability for apps to trigger scan requests will be removed in a future * release. */ @Deprecated public boolean isScanAlwaysAvailable() { try { return mService.isScanAlwaysAvailable(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get channel data such as the number of APs found on each channel from the most recent scan. * App requires {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} * * @param executor The executor on which callback will be invoked. * @param resultsCallback A callback that will return {@code List} containing channel * data such as the number of APs found on each channel. * {@link WifiManager#CHANNEL_DATA_KEY_FREQUENCY_MHZ} and * {@link WifiManager#CHANNEL_DATA_KEY_NUM_AP} are used to get * the frequency (Mhz) and number of APs. * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws SecurityException if the caller does not have permission. * @throws NullPointerException if the caller provided invalid inputs. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(NEARBY_WIFI_DEVICES) public void getChannelData(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer> resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { Bundle extras = new Bundle(); extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); mService.getChannelData(new IListListener.Stub() { @Override public void onResult(List value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }, mContext.getOpPackageName(), extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Tell the device to persist the current list of configured networks. *

    * Note: It is possible for this method to change the network IDs of * existing networks. You should assume the network IDs can be different * after calling this method. * * @return {@code false}. * @deprecated There is no need to call this method - * {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)} * and {@link #removeNetwork(int)} already persist the configurations automatically. */ @Deprecated public boolean saveConfiguration() { return false; } /** * Helper class to support driver country code changed listener. */ private static class OnDriverCountryCodeChangedProxy extends IOnWifiDriverCountryCodeChangedListener.Stub { @NonNull private Executor mExecutor; @NonNull private ActiveCountryCodeChangedCallback mCallback; OnDriverCountryCodeChangedProxy(@NonNull Executor executor, @NonNull ActiveCountryCodeChangedCallback callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); mExecutor = executor; mCallback = callback; } @Override public void onDriverCountryCodeChanged(String countryCode) { Log.i(TAG, "OnDriverCountryCodeChangedProxy: receive onDriverCountryCodeChanged: " + countryCode); Binder.clearCallingIdentity(); if (countryCode != null) { mExecutor.execute(() -> mCallback.onActiveCountryCodeChanged(countryCode)); } else { mExecutor.execute(() -> mCallback.onCountryCodeInactive()); } } } /** * Interface used to listen the active country code changed event. * @hide */ @SystemApi public interface ActiveCountryCodeChangedCallback { /** * Called when the country code used by the Wi-Fi subsystem has changed. * * @param countryCode An ISO-3166-alpha2 country code which is 2-Character alphanumeric. */ void onActiveCountryCodeChanged(@NonNull String countryCode); /** * Called when the Wi-Fi subsystem does not have an active country code. * This can happen when Wi-Fi is disabled. */ void onCountryCodeInactive(); } /** * Add the provided callback for the active country code changed event. * Caller will receive either * {@link WifiManager.ActiveCountryCodeChangedCallback#onActiveCountryCodeChanged(String)} * or {@link WifiManager.ActiveCountryCodeChangedCallback#onCountryCodeInactive()} * on registration. * * Note: When the global location setting is off or the caller does not have runtime location * permission, caller will not receive the callback even if caller register callback succeeded. * * * Caller can remove a previously registered callback using * {@link WifiManager#unregisterActiveCountryCodeChangedCallback( * ActiveCountryCodeChangedCallback)}. * *

    * Note: * The value provided by * {@link WifiManager.ActiveCountryCodeChangedCallback#onActiveCountryCodeChanged(String)} * may be different from the returned value from {@link WifiManager#getCountryCode()} even if * the Wi-Fi subsystem is active. See: {@link WifiManager#getCountryCode()} for details. *

    * * @param executor The Executor on which to execute the callbacks. * @param callback callback for the driver country code changed events. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void registerActiveCountryCodeChangedCallback( @NonNull @CallbackExecutor Executor executor, @NonNull ActiveCountryCodeChangedCallback callback) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); if (mVerboseLoggingEnabled) { Log.d(TAG, "registerActiveCountryCodeChangedCallback: callback=" + callback + ", executor=" + executor); } final int callbackIdentifier = System.identityHashCode(callback); synchronized (sActiveCountryCodeChangedCallbackMap) { try { IOnWifiDriverCountryCodeChangedListener.Stub binderListener = new OnDriverCountryCodeChangedProxy(executor, callback); sActiveCountryCodeChangedCallbackMap.put(callbackIdentifier, binderListener); mService.registerDriverCountryCodeChangedListener(binderListener, mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { sActiveCountryCodeChangedCallbackMap.remove(callbackIdentifier); throw e.rethrowFromSystemServer(); } } } /** * Allow callers to remove a previously registered listener. After calling this method, * applications will no longer receive the active country code changed events through that * callback. * * @param callback Callback to remove the active country code changed events. * * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @SystemApi public void unregisterActiveCountryCodeChangedCallback( @NonNull ActiveCountryCodeChangedCallback callback) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } if (callback == null) throw new IllegalArgumentException("Callback cannot be null"); if (mVerboseLoggingEnabled) { Log.d(TAG, "unregisterActiveCountryCodeChangedCallback: callback=" + callback); } final int callbackIdentifier = System.identityHashCode(callback); synchronized (sActiveCountryCodeChangedCallbackMap) { try { if (!sActiveCountryCodeChangedCallbackMap.contains(callbackIdentifier)) { Log.w(TAG, "Unknown external listener " + callbackIdentifier); return; } mService.unregisterDriverCountryCodeChangedListener( sActiveCountryCodeChangedCallbackMap.get(callbackIdentifier)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { sActiveCountryCodeChangedCallbackMap.remove(callbackIdentifier); } } } /** * Interface used to listen to changes in current network state. * @hide */ @SystemApi public interface WifiNetworkStateChangedListener { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"WIFI_ROLE_CLIENT_"}, value = { WIFI_ROLE_CLIENT_PRIMARY, WIFI_ROLE_CLIENT_SECONDARY_INTERNET, WIFI_ROLE_CLIENT_SECONDARY_LOCAL_ONLY }) @interface WifiClientModeRole {} /** * A client mode role returned by {@link #onWifiNetworkStateChanged(int, int)}. * Represents the primary Client Mode Manager which is mostly used for internet, but could * also be used for other use-cases such as local only connections. **/ int WIFI_ROLE_CLIENT_PRIMARY = 1; /** * A client mode role returned by {@link #onWifiNetworkStateChanged(int, int)}. * Represents a Client Mode Manager dedicated for the secondary internet use-case. **/ int WIFI_ROLE_CLIENT_SECONDARY_INTERNET = 2; /** * A client mode role returned by {@link #onWifiNetworkStateChanged(int, int)}. * Represents a Client Mode Manager dedicated for the local only connection use-case. **/ int WIFI_ROLE_CLIENT_SECONDARY_LOCAL_ONLY = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"WIFI_NETWORK_STATUS_"}, value = { WIFI_NETWORK_STATUS_IDLE, WIFI_NETWORK_STATUS_SCANNING, WIFI_NETWORK_STATUS_CONNECTING, WIFI_NETWORK_STATUS_AUTHENTICATING, WIFI_NETWORK_STATUS_OBTAINING_IPADDR, WIFI_NETWORK_STATUS_CONNECTED, WIFI_NETWORK_STATUS_DISCONNECTED, WIFI_NETWORK_STATUS_FAILED }) @interface WifiNetworkState {} /** * A state returned by {@link #onWifiNetworkStateChanged(int, int)}. * Supplicant is in uninitialized state. **/ int WIFI_NETWORK_STATUS_IDLE = 1; /** * A state returned by {@link #onWifiNetworkStateChanged(int, int)}. * Supplicant is scanning. **/ int WIFI_NETWORK_STATUS_SCANNING = 2; /** * A state returned by {@link #onWifiNetworkStateChanged(int, int)}. * L2 connection is in progress. **/ int WIFI_NETWORK_STATUS_CONNECTING = 3; /** * A state returned by {@link #onWifiNetworkStateChanged(int, int)}. * L2 connection 4 way handshake. **/ int WIFI_NETWORK_STATUS_AUTHENTICATING = 4; /** * A state returned by {@link #onWifiNetworkStateChanged(int, int)}. * L2 connection complete. Obtaining IP address. **/ int WIFI_NETWORK_STATUS_OBTAINING_IPADDR = 5; /** * A state returned by {@link #onWifiNetworkStateChanged(int, int)}. * L3 connection is complete. **/ int WIFI_NETWORK_STATUS_CONNECTED = 6; /** * A state returned by {@link #onWifiNetworkStateChanged(int, int)}. * Network disconnected. **/ int WIFI_NETWORK_STATUS_DISCONNECTED = 7; /** * A state returned by {@link #onWifiNetworkStateChanged(int, int)}. * A pseudo-state that should normally never be seen. **/ int WIFI_NETWORK_STATUS_FAILED = 8; /** * Provides network state changes per client mode role. * @param cmmRole the role of the wifi client mode manager having the state change. * One of {@link WifiClientModeRole}. * @param state the wifi network state specified by one of {@link WifiNetworkState}. */ void onWifiNetworkStateChanged(@WifiClientModeRole int cmmRole, @WifiNetworkState int state); } /** * Helper class to support wifi network state changed listener. */ private static class OnWifiNetworkStateChangedProxy extends IWifiNetworkStateChangedListener.Stub { @NonNull private Executor mExecutor; @NonNull private WifiNetworkStateChangedListener mListener; OnWifiNetworkStateChangedProxy(@NonNull Executor executor, @NonNull WifiNetworkStateChangedListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); mExecutor = executor; mListener = listener; } @Override public void onWifiNetworkStateChanged(int cmmRole, int state) { Log.i(TAG, "OnWifiNetworkStateChangedProxy: onWifiNetworkStateChanged: " + cmmRole + ", " + state); Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onWifiNetworkStateChanged(cmmRole, state)); } } /** * Add a listener to listen to Wi-Fi network state changes on available client mode roles * specified in {@link WifiNetworkStateChangedListener.WifiClientModeRole}. * When wifi state changes such as connected/disconnect happens, results will be delivered via * {@link WifiNetworkStateChangedListener#onWifiNetworkStateChanged(int, int)}. * * @param executor The Executor on which to execute the callbacks. * @param listener listener for the network status updates. * @throws SecurityException if the caller is missing required permissions. * @throws IllegalArgumentException if incorrect input arguments are provided. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.NETWORK_SETTINGS) public void addWifiNetworkStateChangedListener(@NonNull @CallbackExecutor Executor executor, @NonNull WifiNetworkStateChangedListener listener) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (listener == null) throw new IllegalArgumentException("listener cannot be null"); if (mVerboseLoggingEnabled) { Log.d(TAG, "addWifiNetworkStateChangedListener: listener=" + listener + ", executor=" + executor); } final int listenerIdentifier = System.identityHashCode(listener); synchronized (sOnWifiNetworkStateChangedListenerMap) { try { IWifiNetworkStateChangedListener.Stub listenerProxy = new OnWifiNetworkStateChangedProxy(executor, listener); sOnWifiNetworkStateChangedListenerMap.put(listenerIdentifier, listenerProxy); mService.addWifiNetworkStateChangedListener(listenerProxy); } catch (RemoteException e) { sOnWifiNetworkStateChangedListenerMap.remove(listenerIdentifier); throw e.rethrowFromSystemServer(); } } } /** * Remove a listener added using * {@link #addWifiNetworkStateChangedListener(Executor, WifiNetworkStateChangedListener)}. * @param listener the listener to be removed. * @throws IllegalArgumentException if incorrect input arguments are provided. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void removeWifiNetworkStateChangedListener( @NonNull WifiNetworkStateChangedListener listener) { if (listener == null) throw new IllegalArgumentException("listener cannot be null"); if (mVerboseLoggingEnabled) { Log.d(TAG, "removeWifiNetworkStateChangedListener: listener=" + listener); } final int listenerIdentifier = System.identityHashCode(listener); synchronized (sOnWifiNetworkStateChangedListenerMap) { try { if (!sOnWifiNetworkStateChangedListenerMap.contains(listenerIdentifier)) { Log.w(TAG, "Unknown external listener " + listenerIdentifier); return; } mService.removeWifiNetworkStateChangedListener( sOnWifiNetworkStateChangedListenerMap.get(listenerIdentifier)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { sOnWifiNetworkStateChangedListenerMap.remove(listenerIdentifier); } } } /** * Get the country code as resolved by the Wi-Fi framework. * The Wi-Fi framework uses multiple sources to resolve a country code * - in order of priority (high to low): * 1. Override country code set by {@link WifiManager#setOverrideCountryCode(String)} * and cleared by {@link WifiManager#clearOverrideCountryCode()}. Typically only used * for testing. * 2. Country code supplied by the telephony module. Typically provided from the * current network or from emergency cell information. * 3. Country code supplied by the wifi driver module. (802.11d) * 4. Default country code set either via {@code ro.boot.wificountrycode} * or the {@link WifiManager#setDefaultCountryCode(String)}. * *

    * Note: * This method returns the Country Code value used by the framework - even if not currently * used by the Wi-Fi subsystem. I.e. the returned value from this API may be different from the * value provided by * {@link WifiManager.ActiveCountryCodeChangedCallback#onActiveCountryCodeChanged(String)}. * Such a difference may happen when there is an ongoing network connection (STA, AP, Direct, * or Aware) and the Wi-Fi subsystem does not support dynamic updates - at that point the * framework may defer setting the Country Code to the Wi-Fi subsystem. *

    * @return the country code in ISO 3166 alpha-2 (2-letter) upper format, * or null if there is no country code configured. * * @hide */ @Nullable @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.ACCESS_COARSE_LOCATION }) public String getCountryCode() { try { return mService.getCountryCode(mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Set the override country code - may be used for testing. See the country code resolution * order and format in {@link #getCountryCode()}. * @param country A 2-Character alphanumeric country code. * @see #getCountryCode(). * * @hide */ @RequiresApi(Build.VERSION_CODES.S) @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_COUNTRY_CODE) public void setOverrideCountryCode(@NonNull String country) { try { mService.setOverrideCountryCode(country); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This clears the override country code which was previously set by * {@link WifiManager#setOverrideCountryCode(String)} method. * @see #getCountryCode(). * * @hide */ @RequiresApi(Build.VERSION_CODES.S) @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_COUNTRY_CODE) public void clearOverrideCountryCode() { try { mService.clearOverrideCountryCode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Used to configure the default country code. See {@link #getCountryCode()} for resolution * method of the country code. * @param country A 2-character alphanumeric country code. * @see #getCountryCode(). * * @hide */ @RequiresApi(Build.VERSION_CODES.S) @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_COUNTRY_CODE) public void setDefaultCountryCode(@NonNull String country) { try { mService.setDefaultCountryCode(country); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return the DHCP-assigned addresses from the last successful DHCP request, * if any. * * @return the DHCP information * * @deprecated Use the methods on {@link android.net.LinkProperties} which can be obtained * either via {@link NetworkCallback#onLinkPropertiesChanged(Network, LinkProperties)} or * {@link ConnectivityManager#getLinkProperties(Network)}. * *

    * Compatibility Notes: *

  • On devices supporting concurrent connections (indicated via * {@link #isStaConcurrencyForLocalOnlyConnectionsSupported()}, etc), this API will return * the details of the internet providing connection (if any) to all apps, except for the apps * that triggered the creation of the concurrent connection. For such apps, this API will return * the details of the connection they created. e.g. apps using {@link WifiNetworkSpecifier} will * trigger a concurrent connection on supported devices and hence this API will provide * details of their peer to peer connection (not the internet providing connection). This * is to maintain backwards compatibility with behavior on single STA devices.
  • *

    */ @Deprecated public DhcpInfo getDhcpInfo() { try { return mService.getDhcpInfo(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable or disable Wi-Fi. *

    * Applications must have the {@link android.Manifest.permission#CHANGE_WIFI_STATE} * permission to toggle wifi. * * @param enabled {@code true} to enable, {@code false} to disable. * @return {@code false} if the request cannot be satisfied; {@code true} indicates that wifi is * either already in the requested state, or in progress toward the requested state. * @throws SecurityException if the caller is missing required permissions. * * @deprecated Starting with Build.VERSION_CODES#Q, applications are not allowed to * enable/disable Wi-Fi. * Compatibility Note: For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always fail and return * {@code false}. If apps are targeting an older SDK ({@link android.os.Build.VERSION_CODES#P} * or below), they can continue to use this API. *

    * Deprecation Exemptions: *

      *
    • Device Owner (DO), Profile Owner (PO) and system apps. *
    * * Starting with {@link android.os.Build.VERSION_CODES#TIRAMISU}, DO/COPE may set * a user restriction (DISALLOW_CHANGE_WIFI_STATE) to only allow DO/PO to use this API. */ @Deprecated public boolean setWifiEnabled(boolean enabled) { try { return mService.setWifiEnabled(mContext.getOpPackageName(), enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Abstract callback class for applications to receive updates about the Wi-Fi subsystem * restarting. The Wi-Fi subsystem can restart due to internal recovery mechanisms or via user * action. */ @RequiresApi(Build.VERSION_CODES.S) public abstract static class SubsystemRestartTrackingCallback { private final SubsystemRestartTrackingCallback.SubsystemRestartCallbackProxy mProxy; public SubsystemRestartTrackingCallback() { mProxy = new SubsystemRestartTrackingCallback.SubsystemRestartCallbackProxy(); } /*package*/ @NonNull SubsystemRestartTrackingCallback.SubsystemRestartCallbackProxy getProxy() { return mProxy; } /** * Indicates that the Wi-Fi subsystem is about to restart. */ public abstract void onSubsystemRestarting(); /** * Indicates that the Wi-Fi subsystem has restarted. */ public abstract void onSubsystemRestarted(); private static class SubsystemRestartCallbackProxy extends ISubsystemRestartCallback.Stub { private final Object mLock = new Object(); @Nullable @GuardedBy("mLock") private Executor mExecutor; @Nullable @GuardedBy("mLock") private SubsystemRestartTrackingCallback mCallback; SubsystemRestartCallbackProxy() { mExecutor = null; mCallback = null; } /*package*/ void initProxy(@NonNull Executor executor, @NonNull SubsystemRestartTrackingCallback callback) { synchronized (mLock) { mExecutor = executor; mCallback = callback; } } /*package*/ void cleanUpProxy() { synchronized (mLock) { mExecutor = null; mCallback = null; } } @Override public void onSubsystemRestarting() { Executor executor; SubsystemRestartTrackingCallback callback; synchronized (mLock) { executor = mExecutor; callback = mCallback; } if (executor == null || callback == null) { return; } Binder.clearCallingIdentity(); executor.execute(callback::onSubsystemRestarting); } @Override public void onSubsystemRestarted() { Executor executor; SubsystemRestartTrackingCallback callback; synchronized (mLock) { executor = mExecutor; callback = mCallback; } if (executor == null || callback == null) { return; } Binder.clearCallingIdentity(); executor.execute(callback::onSubsystemRestarted); } } } /** * Registers a {@link SubsystemRestartTrackingCallback} to listen to Wi-Fi subsystem restarts. * The subsystem may restart due to internal recovery mechanisms or via user action. * * @see #unregisterSubsystemRestartTrackingCallback(SubsystemRestartTrackingCallback) * * @param executor Executor to execute callback on * @param callback {@link SubsystemRestartTrackingCallback} to register */ @RequiresApi(Build.VERSION_CODES.S) @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void registerSubsystemRestartTrackingCallback( @NonNull @CallbackExecutor Executor executor, @NonNull SubsystemRestartTrackingCallback callback) { if (executor == null) throw new IllegalArgumentException("executor must not be null"); if (callback == null) throw new IllegalArgumentException("callback must not be null"); SubsystemRestartTrackingCallback.SubsystemRestartCallbackProxy proxy = callback.getProxy(); proxy.initProxy(executor, callback); try { mService.registerSubsystemRestartCallback(proxy); } catch (RemoteException e) { proxy.cleanUpProxy(); throw e.rethrowFromSystemServer(); } } /** * Unregisters a {@link SubsystemRestartTrackingCallback} registered with * {@link #registerSubsystemRestartTrackingCallback(Executor, SubsystemRestartTrackingCallback)} * * @param callback {@link SubsystemRestartTrackingCallback} to unregister */ @RequiresApi(Build.VERSION_CODES.S) @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void unregisterSubsystemRestartTrackingCallback( @NonNull SubsystemRestartTrackingCallback callback) { if (callback == null) throw new IllegalArgumentException("callback must not be null"); SubsystemRestartTrackingCallback.SubsystemRestartCallbackProxy proxy = callback.getProxy(); try { mService.unregisterSubsystemRestartCallback(proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { proxy.cleanUpProxy(); } } /** * Restart the Wi-Fi subsystem. * * Restarts the Wi-Fi subsystem - effectively disabling it and re-enabling it. All existing * Access Point (AP) associations are torn down, all Soft APs are disabled, Wi-Fi Direct and * Wi-Fi Aware are disabled. * * The state of the system after restart is not guaranteed to match its state before the API is * called - for instance the device may associate to a different Access Point (AP), and tethered * hotspots may or may not be restored. * * Use the * {@link #registerSubsystemRestartTrackingCallback(Executor, SubsystemRestartTrackingCallback)} * to track the operation. * * @hide */ @RequiresApi(Build.VERSION_CODES.S) @SystemApi @RequiresPermission(android.Manifest.permission.RESTART_WIFI_SUBSYSTEM) public void restartWifiSubsystem() { try { mService.restartWifiSubsystem(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Gets the Wi-Fi enabled state. * @return One of {@link #WIFI_STATE_DISABLED}, * {@link #WIFI_STATE_DISABLING}, {@link #WIFI_STATE_ENABLED}, * {@link #WIFI_STATE_ENABLING}, {@link #WIFI_STATE_UNKNOWN} * @see #isWifiEnabled() */ public int getWifiState() { try { return mService.getWifiEnabledState(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return whether Wi-Fi is enabled or disabled. * @return {@code true} if Wi-Fi is enabled * @see #getWifiState() */ public boolean isWifiEnabled() { return getWifiState() == WIFI_STATE_ENABLED; } /** * Calculates the level of the signal. This should be used any time a signal * is being shown. * * @param rssi The power of the signal measured in RSSI. * @param numLevels The number of levels to consider in the calculated level. * @return A level of the signal, given in the range of 0 to numLevels-1 (both inclusive). * @deprecated Callers should use {@link #calculateSignalLevel(int)} instead to get the * signal level using the system default RSSI thresholds, or otherwise compute the RSSI level * themselves using their own formula. */ @Deprecated public static int calculateSignalLevel(int rssi, int numLevels) { if (rssi <= MIN_RSSI) { return 0; } else if (rssi >= MAX_RSSI) { return numLevels - 1; } else { float inputRange = (MAX_RSSI - MIN_RSSI); float outputRange = (numLevels - 1); return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange); } } /** * Given a raw RSSI, return the RSSI signal quality rating using the system default RSSI * quality rating thresholds. * @param rssi a raw RSSI value, in dBm, usually between -55 and -90 * @return the RSSI signal quality rating, in the range * [0, {@link #getMaxSignalLevel()}], where 0 is the lowest (worst signal) RSSI * rating and {@link #getMaxSignalLevel()} is the highest (best signal) RSSI rating. */ @IntRange(from = 0) public int calculateSignalLevel(int rssi) { try { return mService.calculateSignalLevel(rssi); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the system default maximum signal level. * This is the maximum RSSI level returned by {@link #calculateSignalLevel(int)}. */ @IntRange(from = 0) public int getMaxSignalLevel() { return calculateSignalLevel(Integer.MAX_VALUE); } /** * Compares two signal strengths. * * @param rssiA The power of the first signal measured in RSSI. * @param rssiB The power of the second signal measured in RSSI. * @return Returns <0 if the first signal is weaker than the second signal, * 0 if the two signals have the same strength, and >0 if the first * signal is stronger than the second signal. */ public static int compareSignalLevel(int rssiA, int rssiB) { return rssiA - rssiB; } /** * Call allowing ConnectivityService to update WifiService with interface mode changes. * * @param ifaceName String name of the updated interface, or null to represent all interfaces * @param mode int representing the new mode, one of: * {@link #IFACE_IP_MODE_TETHERED}, * {@link #IFACE_IP_MODE_LOCAL_ONLY}, * {@link #IFACE_IP_MODE_CONFIGURATION_ERROR}, * {@link #IFACE_IP_MODE_UNSPECIFIED} * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STACK, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK }) public void updateInterfaceIpState(@Nullable String ifaceName, @IfaceIpMode int mode) { try { mService.updateInterfaceIpState(ifaceName, mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /* Wi-Fi/Cellular Coex */ /** * Mandatory coex restriction flag for Wi-Fi Direct. * * @see #setCoexUnsafeChannels(List, int) * * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.S) public static final int COEX_RESTRICTION_WIFI_DIRECT = 0x1 << 0; /** * Mandatory coex restriction flag for SoftAP * * @see #setCoexUnsafeChannels(List, int) * * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.S) public static final int COEX_RESTRICTION_SOFTAP = 0x1 << 1; /** * Mandatory coex restriction flag for Wi-Fi Aware. * * @see #setCoexUnsafeChannels(List, int) * * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.S) public static final int COEX_RESTRICTION_WIFI_AWARE = 0x1 << 2; /** @hide */ @RequiresApi(Build.VERSION_CODES.S) @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = {"COEX_RESTRICTION_"}, value = { COEX_RESTRICTION_WIFI_DIRECT, COEX_RESTRICTION_SOFTAP, COEX_RESTRICTION_WIFI_AWARE }) public @interface CoexRestriction {} /** * @return {@code true} if the default coex algorithm is enabled. {@code false} otherwise. * * @hide */ public boolean isDefaultCoexAlgorithmEnabled() { try { return mService.isDefaultCoexAlgorithmEnabled(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Specify the list of {@link CoexUnsafeChannel} to propagate through the framework for * Wi-Fi/Cellular coex channel avoidance if the default algorithm is disabled via overlay * (i.e. config_wifiCoexDefaultAlgorithmEnabled = false). Otherwise do nothing. * * @param unsafeChannels List of {@link CoexUnsafeChannel} to avoid. * @param restrictions Bitmap of {@code COEX_RESTRICTION_*} constants specifying the mode * restrictions on the specified channels. If any restrictions are set, * then the supplied CoexUnsafeChannels should be completely avoided for * the specified modes, rather than be avoided with best effort. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS) @RequiresApi(Build.VERSION_CODES.S) public void setCoexUnsafeChannels( @NonNull List unsafeChannels, @CoexRestriction int restrictions) { if (unsafeChannels == null) { throw new IllegalArgumentException("unsafeChannels must not be null"); } try { mService.setCoexUnsafeChannels(unsafeChannels, restrictions); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Registers a CoexCallback to listen on the current CoexUnsafeChannels and restrictions being * used for Wi-Fi/cellular coex channel avoidance. The callback method * {@link CoexCallback#onCoexUnsafeChannelsChanged(List, int)} will be called immediately after * registration to return the current values. * * @param executor Executor to execute listener callback on * @param callback CoexCallback to register * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) @RequiresApi(Build.VERSION_CODES.S) public void registerCoexCallback( @NonNull @CallbackExecutor Executor executor, @NonNull CoexCallback callback) { if (executor == null) throw new IllegalArgumentException("executor must not be null"); if (callback == null) throw new IllegalArgumentException("callback must not be null"); CoexCallback.CoexCallbackProxy proxy = callback.getProxy(); proxy.initProxy(executor, callback); try { mService.registerCoexCallback(proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Unregisters a CoexCallback from listening on the current CoexUnsafeChannels and restrictions * being used for Wi-Fi/cellular coex channel avoidance. * @param callback CoexCallback to unregister * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) @RequiresApi(Build.VERSION_CODES.S) public void unregisterCoexCallback(@NonNull CoexCallback callback) { if (callback == null) throw new IllegalArgumentException("callback must not be null"); CoexCallback.CoexCallbackProxy proxy = callback.getProxy(); try { mService.unregisterCoexCallback(proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { proxy.cleanUpProxy(); } } /** * Abstract callback class for applications to receive updates about current CoexUnsafeChannels * for Wi-Fi/Cellular coex channel avoidance. * * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.S) public abstract static class CoexCallback { private final CoexCallbackProxy mCoexCallbackProxy; public CoexCallback() { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } mCoexCallbackProxy = new CoexCallbackProxy(); } /*package*/ @NonNull CoexCallbackProxy getProxy() { return mCoexCallbackProxy; } /** * This indicates the current CoexUnsafeChannels and restrictions calculated by the default * coex algorithm if config_wifiCoexDefaultAlgorithmEnabled is {@code true}. Otherwise, the * values will match the ones supplied to {@link #setCoexUnsafeChannels(List, int)}. * * @param unsafeChannels List of {@link CoexUnsafeChannel} to avoid. * @param restrictions Bitmap of {@code COEX_RESTRICTION_*} constants specifying the mode * restrictions on the specified channels. If any restrictions are set, * then the supplied CoexUnsafeChannels should be completely avoided for * the specified modes, rather than be avoided with best effort. */ public abstract void onCoexUnsafeChannelsChanged( @NonNull List unsafeChannels, @CoexRestriction int restrictions); /** * Callback proxy for CoexCallback objects. */ private static class CoexCallbackProxy extends ICoexCallback.Stub { private final Object mLock = new Object(); @Nullable @GuardedBy("mLock") private Executor mExecutor; @Nullable @GuardedBy("mLock") private CoexCallback mCallback; CoexCallbackProxy() { mExecutor = null; mCallback = null; } /*package*/ void initProxy(@NonNull Executor executor, @NonNull CoexCallback callback) { synchronized (mLock) { mExecutor = executor; mCallback = callback; } } /*package*/ void cleanUpProxy() { synchronized (mLock) { mExecutor = null; mCallback = null; } } @Override public void onCoexUnsafeChannelsChanged( @NonNull List unsafeChannels, @CoexRestriction int restrictions) { Executor executor; CoexCallback callback; synchronized (mLock) { executor = mExecutor; callback = mCallback; } if (executor == null || callback == null) { return; } Binder.clearCallingIdentity(); executor.execute(() -> callback.onCoexUnsafeChannelsChanged(unsafeChannels, restrictions)); } } } /** * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration. * Note that starting Soft AP mode may disable station mode operation if the device does not * support concurrency. * @param wifiConfig SSID, security and channel details as part of WifiConfiguration, or null to * use the persisted Soft AP configuration that was previously set using * {@link #setWifiApConfiguration(WifiConfiguration)}. * @return {@code true} if the operation succeeded, {@code false} otherwise * * @hide */ @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STACK, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK }) public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) { try { return mService.startSoftAp(wifiConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration. * Note that starting Soft AP mode may disable station mode operation if the device does not * support concurrency. * * Note: Call {@link WifiManager#validateSoftApConfiguration(SoftApConfiguration)} to avoid * unexpected error due to invalid configuration. * * @param softApConfig A valid SoftApConfiguration specifying the configuration of the SAP, or * null to use the persisted Soft AP configuration that was previously set * using {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)}. * @return {@code true} if the operation succeeded, {@code false} otherwise * * @deprecated Use {@link #startTetheredHotspot(TetheringManager.TetheringRequest)} * instead. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STACK, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK }) @Deprecated public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) { try { return mService.startTetheredHotspot(softApConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Start Soft AP (hotspot) mode for tethering purposes with the specified TetheringRequest. * Note that starting Soft AP mode may disable station mode operation if the device does not * support concurrency. * * @param request A valid TetheringRequest specifying the configuration of the SAP. * @param executor Executor to run the callback on. * @param callback Callback to listen on state changes for this specific request. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STACK, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK }) public void startTetheredHotspot(@NonNull TetheringManager.TetheringRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull SoftApCallback callback) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); ISoftApCallback.Stub binderCallback = new SoftApCallbackProxy(executor, callback, IFACE_IP_MODE_TETHERED); try { mService.startTetheredHotspotRequest(request, binderCallback, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Stop SoftAp mode. * Note that stopping softap mode will restore the previous wifi mode. * @return {@code true} if the operation succeeds, {@code false} otherwise * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STACK, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK }) public boolean stopSoftAp() { try { return mService.stopSoftAp(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if input configuration is valid. * * @param config a configuration would like to be checked. * @return true if config is valid, otherwise false. */ public boolean validateSoftApConfiguration(@NonNull SoftApConfiguration config) { if (config == null) { throw new IllegalArgumentException(TAG + ": config can not be null"); } try { return mService.validateSoftApConfiguration(config); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Request a local only hotspot that an application can use to communicate between co-located * devices connected to the created WiFi hotspot. The network created by this method will not * have Internet access. Each application can make a single request for the hotspot, but * multiple applications could be requesting the hotspot at the same time. When multiple * applications have successfully registered concurrently, they will be sharing the underlying * hotspot. {@link LocalOnlyHotspotCallback#onStarted(LocalOnlyHotspotReservation)} is called * when the hotspot is ready for use by the application. *

    * Each application can make a single active call to this method. The {@link * LocalOnlyHotspotCallback#onStarted(LocalOnlyHotspotReservation)} callback supplies the * requestor with a {@link LocalOnlyHotspotReservation} that contains a * {@link SoftApConfiguration} with the SSID, security type and credentials needed to connect * to the hotspot. Communicating this information is up to the application. *

    * If the LocalOnlyHotspot cannot be created, the {@link LocalOnlyHotspotCallback#onFailed(int)} * method will be called. Example failures include errors bringing up the network or if * there is an incompatible operating mode. For example, if the user is currently using Wifi * Tethering to provide an upstream to another device, LocalOnlyHotspot may not start due to * an incompatible mode. The possible error codes include: * {@link LocalOnlyHotspotCallback#ERROR_NO_CHANNEL}, * {@link LocalOnlyHotspotCallback#ERROR_GENERIC}, * {@link LocalOnlyHotspotCallback#ERROR_INCOMPATIBLE_MODE} and * {@link LocalOnlyHotspotCallback#ERROR_TETHERING_DISALLOWED}. *

    * Internally, requests will be tracked to prevent the hotspot from being torn down while apps * are still using it. The {@link LocalOnlyHotspotReservation} object passed in the {@link * LocalOnlyHotspotCallback#onStarted(LocalOnlyHotspotReservation)} call should be closed when * the LocalOnlyHotspot is no longer needed using {@link LocalOnlyHotspotReservation#close()}. * Since the hotspot may be shared among multiple applications, removing the final registered * application request will trigger the hotspot teardown. This means that applications should * not listen to broadcasts containing wifi state to determine if the hotspot was stopped after * they are done using it. Additionally, once {@link LocalOnlyHotspotReservation#close()} is * called, applications will not receive callbacks of any kind. *

    * Applications should be aware that the user may also stop the LocalOnlyHotspot through the * Settings UI; it is not guaranteed to stay up as long as there is a requesting application. * The requestors will be notified of this case via * {@link LocalOnlyHotspotCallback#onStopped()}. Other cases may arise where the hotspot is * torn down (Emergency mode, etc). Application developers should be aware that it can stop * unexpectedly, but they will receive a notification if they have properly registered. *

    * Applications should also be aware that this network will be shared with other applications. * Applications are responsible for protecting their data on this network (e.g. TLS). *

    * Applications targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later need to have * the following permissions: {@link android.Manifest.permission#CHANGE_WIFI_STATE} and * {@link android.Manifest.permission#NEARBY_WIFI_DEVICES}. * Applications targeting {@link Build.VERSION_CODES#S} or prior SDK levels need to have the * following permissions: {@link android.Manifest.permission#CHANGE_WIFI_STATE} and * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} * Callers without * the permissions will trigger a {@link java.lang.SecurityException}. *

    * @param callback LocalOnlyHotspotCallback for the application to receive updates about * operating status. * @param handler Handler to be used for callbacks. If the caller passes a null Handler, the * main thread will be used. */ @RequiresPermission(allOf = {CHANGE_WIFI_STATE, ACCESS_FINE_LOCATION, NEARBY_WIFI_DEVICES}, conditional = true) public void startLocalOnlyHotspot(LocalOnlyHotspotCallback callback, @Nullable Handler handler) { Executor executor = handler == null ? null : new HandlerExecutor(handler); startLocalOnlyHotspotInternal(null, executor, callback); } /** * Starts a local-only hotspot with a specific configuration applied. See * {@link #startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)}. * * Applications need either {@link android.Manifest.permission#NETWORK_SETUP_WIZARD}, * {@link android.Manifest.permission#NETWORK_SETTINGS} or * {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} to call this method. * * Since custom configuration settings may be incompatible with each other, the hotspot started * through this method cannot coexist with another hotspot created through * startLocalOnlyHotspot. If this is attempted, the first hotspot request wins and others * receive {@link LocalOnlyHotspotCallback#ERROR_GENERIC} through * {@link LocalOnlyHotspotCallback#onFailed}. * * @param config Custom configuration for the hotspot. See {@link SoftApConfiguration}. * @param executor Executor to run callback methods on, or null to use the main thread. * @param callback Callback object for updates about hotspot status, or null for no updates. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, NEARBY_WIFI_DEVICES}) public void startLocalOnlyHotspot(@NonNull SoftApConfiguration config, @Nullable @CallbackExecutor Executor executor, @Nullable LocalOnlyHotspotCallback callback) { Objects.requireNonNull(config); startLocalOnlyHotspotInternal(config, executor, callback); } /** * Common implementation of both configurable and non-configurable LOHS. * * @param config App-specified configuration, or null. When present, additional privileges are * required, and the hotspot cannot be shared with other clients. * @param executor Executor to run callback methods on, or null to use the main thread. * @param callback Callback object for updates about hotspot status, or null for no updates. */ private void startLocalOnlyHotspotInternal( @Nullable SoftApConfiguration config, @Nullable @CallbackExecutor Executor executor, @Nullable LocalOnlyHotspotCallback callback) { if (executor == null) { executor = mContext.getMainExecutor(); } synchronized (mLock) { LocalOnlyHotspotCallbackProxy proxy = new LocalOnlyHotspotCallbackProxy(this, executor, callback); try { String packageName = mContext.getOpPackageName(); String featureId = mContext.getAttributionTag(); Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } int returnCode = mService.startLocalOnlyHotspot(proxy, packageName, featureId, config, extras); if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) { // Send message to the proxy to make sure we call back on the correct thread proxy.onHotspotFailed(returnCode); return; } mLOHSCallbackProxy = proxy; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Cancels a pending local only hotspot request. This can be used by the calling application to * cancel the existing request if the provided callback has not been triggered. Calling this * method will be equivalent to closing the returned LocalOnlyHotspotReservation, but it is not * explicitly required. *

    * When cancelling this request, application developers should be aware that there may still be * outstanding local only hotspot requests and the hotspot may still start, or continue running. * Additionally, if a callback was registered, it will no longer be triggered after calling * cancel. * * @hide */ @UnsupportedAppUsage public void cancelLocalOnlyHotspotRequest() { synchronized (mLock) { stopLocalOnlyHotspot(); } } /** * Method used to inform WifiService that the LocalOnlyHotspot is no longer needed. This * method is used by WifiManager to release LocalOnlyHotspotReservations held by calling * applications and removes the internal tracking for the hotspot request. When all requesting * applications are finished using the hotspot, it will be stopped and WiFi will return to the * previous operational mode. * * This method should not be called by applications. Instead, they should call the close() * method on their LocalOnlyHotspotReservation. */ private void stopLocalOnlyHotspot() { synchronized (mLock) { if (mLOHSCallbackProxy == null) { // nothing to do, the callback was already cleaned up. return; } mLOHSCallbackProxy = null; try { mService.stopLocalOnlyHotspot(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Registers a callback for local only hotspot. See {@link SoftApCallback}. Caller will receive * the following callbacks on registration: *

      *
    • {@link SoftApCallback#onStateChanged(int, int)}
    • *
    • {@link SoftApCallback#onConnectedClientsChanged(List)}
    • *
    • {@link SoftApCallback#onInfoChanged(List)}
    • *
    • {@link SoftApCallback#onCapabilityChanged(SoftApCapability)}
    • *
    * * Use {@link SoftApCallback#onConnectedClientsChanged(SoftApInfo, List)} to know * if there are any clients connected to a specific bridged instance of this AP * (if bridged AP is enabled). * * Note: Caller will receive the callback * {@link SoftApCallback#onConnectedClientsChanged(SoftApInfo, List)} * on registration when there are clients connected to AP. * * These will be dispatched on registration to provide the caller with the current state * (and are not an indication of any current change). Note that receiving an immediate * WIFI_AP_STATE_FAILED value for soft AP state indicates that the latest attempt to start * soft AP has failed. Caller can unregister a previously registered callback using * {@link #unregisterLocalOnlyHotspotSoftApCallback} *

    * * @param executor The Executor on whose thread to execute the callbacks of the {@code callback} * object. * @param callback Callback for local only hotspot events * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(NEARBY_WIFI_DEVICES) public void registerLocalOnlyHotspotSoftApCallback(@NonNull @CallbackExecutor Executor executor, @NonNull SoftApCallback callback) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerLocalOnlyHotspotSoftApCallback: callback=" + callback + ", executor=" + executor); try { synchronized (sLocalOnlyHotspotSoftApCallbackMap) { ISoftApCallback.Stub binderCallback = new SoftApCallbackProxy(executor, callback, IFACE_IP_MODE_LOCAL_ONLY); sLocalOnlyHotspotSoftApCallbackMap.put(System.identityHashCode(callback), binderCallback); Bundle extras = new Bundle(); extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); mService.registerLocalOnlyHotspotSoftApCallback(binderCallback, extras); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to unregister a previously registered callback. After calling this method, * applications will no longer receive local only hotspot events. * *

    * * @param callback Callback to unregister for soft AP events * * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(NEARBY_WIFI_DEVICES) public void unregisterLocalOnlyHotspotSoftApCallback(@NonNull SoftApCallback callback) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); } if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "unregisterLocalOnlyHotspotSoftApCallback: callback=" + callback); try { synchronized (sLocalOnlyHotspotSoftApCallbackMap) { int callbackIdentifier = System.identityHashCode(callback); if (!sLocalOnlyHotspotSoftApCallbackMap.contains(callbackIdentifier)) { Log.w(TAG, "Unknown external callback " + callbackIdentifier); return; } Bundle extras = new Bundle(); extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); mService.unregisterLocalOnlyHotspotSoftApCallback( sLocalOnlyHotspotSoftApCallbackMap.get(callbackIdentifier), extras); sLocalOnlyHotspotSoftApCallbackMap.remove(callbackIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers (Settings UI) to watch LocalOnlyHotspot state changes. Callers will * receive a {@link LocalOnlyHotspotSubscription} object as a parameter of the * {@link LocalOnlyHotspotObserver#onRegistered(LocalOnlyHotspotSubscription)}. The registered * callers will receive the {@link LocalOnlyHotspotObserver#onStarted(SoftApConfiguration)} and * {@link LocalOnlyHotspotObserver#onStopped()} callbacks. *

    * Applications should have the * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} * permission. Callers without the permission will trigger a * {@link java.lang.SecurityException}. *

    * @param observer LocalOnlyHotspotObserver callback. * @param handler Handler to use for callbacks * * @hide */ public void watchLocalOnlyHotspot(LocalOnlyHotspotObserver observer, @Nullable Handler handler) { Executor executor = handler == null ? mContext.getMainExecutor() : new HandlerExecutor(handler); synchronized (mLock) { mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, executor, observer); try { mService.startWatchLocalOnlyHotspot(mLOHSObserverProxy); mLOHSObserverProxy.registered(); } catch (RemoteException e) { mLOHSObserverProxy = null; throw e.rethrowFromSystemServer(); } } } /** * Allow callers to stop watching LocalOnlyHotspot state changes. After calling this method, * applications will no longer receive callbacks. * * @hide */ public void unregisterLocalOnlyHotspotObserver() { synchronized (mLock) { if (mLOHSObserverProxy == null) { // nothing to do, the callback was already cleaned up return; } mLOHSObserverProxy = null; try { mService.stopWatchLocalOnlyHotspot(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Gets the tethered Wi-Fi hotspot enabled state. * @return One of {@link #WIFI_AP_STATE_DISABLED}, * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} * @see #isWifiApEnabled() * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState() { try { return mService.getWifiApEnabledState(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return whether tethered Wi-Fi AP is enabled or disabled. * @return {@code true} if tethered Wi-Fi AP is enabled * @see #getWifiApState() * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled() { return getWifiApState() == WIFI_AP_STATE_ENABLED; } /** * Gets the tethered Wi-Fi AP Configuration. * @return AP details in WifiConfiguration * * Note that AP detail may contain configuration which is cannot be represented * by the legacy WifiConfiguration, in such cases a null will be returned. * * @deprecated This API is deprecated. Use {@link #getSoftApConfiguration()} instead. * @hide */ @Nullable @SystemApi @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) @Deprecated public WifiConfiguration getWifiApConfiguration() { try { return mService.getWifiApConfiguration(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Gets the Wi-Fi tethered AP Configuration. * @return AP details in {@link SoftApConfiguration} * * @hide */ @NonNull @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public SoftApConfiguration getSoftApConfiguration() { try { return mService.getSoftApConfiguration(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Gets the last configured Wi-Fi tethered AP passphrase. * * Note: It may be null when there is no passphrase changed since * device boot. * * @param executor The executor on which callback will be invoked. * @param resultCallback An asynchronous callback that will return the last configured * Wi-Fi tethered AP passphrase. * * @throws SecurityException if the caller does not have permission. * @throws NullPointerException if the caller provided invalid inputs. * * @hide */ @Nullable @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void queryLastConfiguredTetheredApPassphraseSinceBoot( @NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultCallback, "resultsCallback cannot be null"); try { mService.queryLastConfiguredTetheredApPassphraseSinceBoot( new IStringListener.Stub() { @Override public void onResult(String value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Sets the tethered Wi-Fi AP Configuration. * @return {@code true} if the operation succeeded, {@code false} otherwise * * @deprecated This API is deprecated. Use {@link #setSoftApConfiguration(SoftApConfiguration)} * instead. * @hide */ @SystemApi @RequiresPermission(CHANGE_WIFI_STATE) @Deprecated public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) { try { return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Sets the tethered Wi-Fi AP Configuration. * * If the API is called while the tethered soft AP is enabled, the configuration will apply to * the current soft AP if the new configuration only includes * {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)} * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)} * or {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)} * or {@link SoftApConfiguration.Builder#setBlockedClientList(List)} * or {@link SoftApConfiguration.Builder#setAllowedClientList(List)} * or {@link SoftApConfiguration.Builder#setAutoShutdownEnabled(boolean)} * or {@link SoftApConfiguration.Builder#setBridgedModeOpportunisticShutdownEnabled(boolean)} * * Otherwise, the configuration changes will be applied when the Soft AP is next started * (the framework will not stop/start the AP). * * Note: Call {@link WifiManager#validateSoftApConfiguration(SoftApConfiguration)} to avoid * unexpected error due to invalid configuration. * * @param softApConfig A valid SoftApConfiguration specifying the configuration of the SAP. * @return {@code true} if the operation succeeded, {@code false} otherwise * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public boolean setSoftApConfiguration(@NonNull SoftApConfiguration softApConfig) { try { return mService.setSoftApConfiguration( softApConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable/Disable TDLS on a specific local route. * *

    * TDLS enables two wireless endpoints to talk to each other directly * without going through the access point that is managing the local * network. It saves bandwidth and improves quality of the link. *

    *

    * This API enables/disables the option of using TDLS. If enabled, the * underlying hardware is free to use TDLS or a hop through the access * point. If disabled, existing TDLS session is torn down and * hardware is restricted to use access point for transferring wireless * packets. Default value for all routes is 'disabled', meaning restricted * to use access point for transferring packets. *

    * * @param remoteIPAddress IP address of the endpoint to setup TDLS with * @param enable true = setup and false = tear down TDLS */ public void setTdlsEnabled(InetAddress remoteIPAddress, boolean enable) { try { mService.enableTdls(remoteIPAddress.getHostAddress(), enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable/Disable TDLS on a specific local route. * * Similar to {@link #setTdlsEnabled(InetAddress, boolean)}, except * this version sends the result of the Enable/Disable request. * * @param remoteIPAddress IP address of the endpoint to setup TDLS with * @param enable true = setup and false = tear down TDLS * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return {@code Boolean} indicating * whether TDLS was successfully enabled or disabled. * {@code true} for success, {@code false} for failure. * * @throws NullPointerException if the caller provided invalid inputs. */ public void setTdlsEnabled(@NonNull InetAddress remoteIPAddress, boolean enable, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); Objects.requireNonNull(remoteIPAddress, "remote IP address cannot be null"); try { mService.enableTdlsWithRemoteIpAddress(remoteIPAddress.getHostAddress(), enable, new IBooleanListener.Stub() { @Override public void onResult(boolean value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Similar to {@link #setTdlsEnabled(InetAddress, boolean) }, except * this version allows you to specify remote endpoint with a MAC address. * @param remoteMacAddress MAC address of the remote endpoint such as 00:00:0c:9f:f2:ab * @param enable true = setup and false = tear down TDLS */ public void setTdlsEnabledWithMacAddress(String remoteMacAddress, boolean enable) { try { mService.enableTdlsWithMacAddress(remoteMacAddress, enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable/Disable TDLS with a specific peer Mac Address. * * Similar to {@link #setTdlsEnabledWithMacAddress(String, boolean)}, except * this version sends the result of the Enable/Disable request. * * @param remoteMacAddress Mac address of the endpoint to setup TDLS with * @param enable true = setup and false = tear down TDLS * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return {@code Boolean} indicating * whether TDLS was successfully enabled or disabled. * {@code true} for success, {@code false} for failure. * * @throws NullPointerException if the caller provided invalid inputs. */ public void setTdlsEnabledWithMacAddress(@NonNull String remoteMacAddress, boolean enable, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); Objects.requireNonNull(remoteMacAddress, "remote Mac address cannot be null"); try { mService.enableTdlsWithRemoteMacAddress(remoteMacAddress, enable, new IBooleanListener.Stub() { @Override public void onResult(boolean value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if a TDLS session can be established at this time via * {@link #setTdlsEnabled(InetAddress, boolean)} or * {@link #setTdlsEnabledWithMacAddress(String, boolean)} or * {@link #setTdlsEnabled(InetAddress, boolean, Executor, Consumer)} or * {@link #setTdlsEnabledWithMacAddress(String, boolean, Executor, Consumer)} * * Internally framework checks the STA connected state, device support for TDLS and * the number of TDLS sessions available in driver/firmware. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return {@code Boolean} indicating * whether a TDLS session can be established at this time. * {@code true} for available, {@code false} for not available. * * @throws NullPointerException if the caller provided invalid inputs. */ public void isTdlsOperationCurrentlyAvailable(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.isTdlsOperationCurrentlyAvailable( new IBooleanListener.Stub() { @Override public void onResult(boolean value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return the maximum number of concurrent TDLS sessions supported by the device. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return the maximum number of * concurrent TDLS sessions supported by the device. Returns * {@code -1} if information is not available, * e.g. if the driver/firmware doesn't provide this information. * * @throws NullPointerException if the caller provided invalid inputs. * @throws UnsupportedOperationException if the feature is not available. */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void getMaxSupportedConcurrentTdlsSessions(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.getMaxSupportedConcurrentTdlsSessions( new IIntegerListener.Stub() { @Override public void onResult(int value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Return the number of currently enabled TDLS sessions. * * Tracks the number of peers enabled for TDLS session via * {@link #setTdlsEnabled(InetAddress, boolean) }, * {@link #setTdlsEnabledWithMacAddress(String, boolean) }, * {@link #setTdlsEnabled(InetAddress, boolean, Executor, Consumer) } and * {@link #setTdlsEnabledWithMacAddress(String, boolean, Executor, Consumer) } * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return the number of Peer * Mac addresses configured in the driver for TDLS session. * * @throws NullPointerException if the caller provided invalid inputs. */ public void getNumberOfEnabledTdlsSessions(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.getNumberOfEnabledTdlsSessions( new IIntegerListener.Stub() { @Override public void onResult(int value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({ActionListener.FAILURE_INTERNAL_ERROR, ActionListener.FAILURE_IN_PROGRESS, ActionListener.FAILURE_BUSY, ActionListener.FAILURE_INVALID_ARGS, ActionListener.FAILURE_NOT_AUTHORIZED}) public @interface ActionListenerFailureReason {} /* WPS specific errors */ /** WPS overlap detected * @deprecated This is deprecated */ public static final int WPS_OVERLAP_ERROR = 3; /** WEP on WPS is prohibited * @deprecated This is deprecated */ public static final int WPS_WEP_PROHIBITED = 4; /** TKIP only prohibited * @deprecated This is deprecated */ public static final int WPS_TKIP_ONLY_PROHIBITED = 5; /** Authentication failure on WPS * @deprecated This is deprecated */ public static final int WPS_AUTH_FAILURE = 6; /** WPS timed out * @deprecated This is deprecated */ public static final int WPS_TIMED_OUT = 7; /** * Interface for callback invocation on an application action. * @hide */ @SystemApi public interface ActionListener { /** * Passed with {@link #onFailure}. * Indicates that the operation failed due to an internal error. */ int FAILURE_INTERNAL_ERROR = 0; /** * Passed with {@link #onFailure}. * Indicates that the operation is already in progress. */ int FAILURE_IN_PROGRESS = 1; /** * Passed with {@link #onFailure}. * Indicates that the operation failed because the framework is busy and is unable to * service the request. */ int FAILURE_BUSY = 2; /** * Passed with {@link #onFailure}. * Indicates that the operation failed due to invalid inputs. */ int FAILURE_INVALID_ARGS = 3; /** * Passed with {@link #onFailure}. * Indicates that the operation failed due to insufficient user permissions. */ int FAILURE_NOT_AUTHORIZED = 4; /** * The operation succeeded. */ void onSuccess(); /** * The operation failed. * @param reason The reason for failure depends on the operation. */ void onFailure(@ActionListenerFailureReason int reason); } /** Interface for callback invocation on a start WPS action * @deprecated This is deprecated */ public static abstract class WpsCallback { /** WPS start succeeded * @deprecated This API is deprecated */ public abstract void onStarted(String pin); /** WPS operation completed successfully * @deprecated This API is deprecated */ public abstract void onSucceeded(); /** * WPS operation failed * @param reason The reason for failure could be one of * {@link #WPS_TKIP_ONLY_PROHIBITED}, {@link #WPS_OVERLAP_ERROR}, * {@link #WPS_WEP_PROHIBITED}, {@link #WPS_TIMED_OUT} or {@link #WPS_AUTH_FAILURE} * and some generic errors. * @deprecated This API is deprecated */ public abstract void onFailed(int reason); } /** * Base class for soft AP callback. Should be extended by applications and set when calling * {@link WifiManager#registerSoftApCallback(Executor, SoftApCallback)}. * * @hide */ @SystemApi public interface SoftApCallback { /** * Called when soft AP state changes. * * @param state the new AP state. One of {@link #WIFI_AP_STATE_DISABLED}, * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} * @param failureReason reason when in failed state. One of * {@link #SAP_START_FAILURE_GENERAL}, * {@link #SAP_START_FAILURE_NO_CHANNEL}, * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}, * {@link #SAP_START_FAILURE_USER_REJECTED} */ default void onStateChanged(@WifiApState int state, @SapStartFailure int failureReason) {} /** * Called when soft AP state changes. *

    * This provides the same state and failure reason as {@link #onStateChanged(int, int)}, but * also provides extra information such as interface name and TetheringRequest in order to * replace usage of the WIFI_AP_STATE_CHANGED_ACTION broadcast. If this method is overridden * then {@link #onStateChanged(int, int)} will no longer be called. * * @param state the new state. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onStateChanged(@NonNull SoftApState state) { onStateChanged(state.mState, state.mFailureReason); } /** * Called when the connected clients to soft AP changes. * * @param clients the currently connected clients * * @deprecated This API is deprecated. * Use {@link #onConnectedClientsChanged(SoftApInfo, List)} instead. */ @Deprecated default void onConnectedClientsChanged(@NonNull List clients) {} /** * Called when the connected clients for a soft AP instance change. * * When the Soft AP is configured in single AP mode, this callback is invoked * with the same {@link SoftApInfo} for all connected clients changes. * When the Soft AP is configured as multiple Soft AP instances (using * {@link SoftApConfiguration.Builder#setBands(int[])} or * {@link SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)}), this * callback is invoked with the corresponding {@link SoftApInfo} for the instance in which * the connected clients changed. * * @param info The {@link SoftApInfo} of the AP. * @param clients The currently connected clients on the AP instance specified by * {@code info}. */ default void onConnectedClientsChanged(@NonNull SoftApInfo info, @NonNull List clients) {} /** * Called when the Soft AP information changes. * * Note: this API remains valid only when the Soft AP is configured as a single AP - * not as multiple Soft APs (which are bridged to each other). When multiple Soft APs are * configured (using {@link SoftApConfiguration.Builder#setBands(int[])} or * {@link SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)}) * this callback will not be triggered - use the * {@link #onInfoChanged(List)} callback in that case. * * @param softApInfo is the Soft AP information. {@link SoftApInfo} * * @deprecated This API is deprecated. Use {@link #onInfoChanged(List)} * instead. */ @Deprecated default void onInfoChanged(@NonNull SoftApInfo softApInfo) { // Do nothing: can be updated to add SoftApInfo details (e.g. channel) to the UI. } /** * Called when the Soft AP information changes. * * Returns information on all configured Soft AP instances. The number of the elements in * the list depends on Soft AP configuration and state: *

      *
    • An empty list will be returned when the Soft AP is disabled. *
    • One information element will be returned in the list when the Soft AP is configured * as a single AP or when a single Soft AP remains active. *
    • Two information elements will be returned in the list when the multiple Soft APs are * configured and are active. * (configured using {@link SoftApConfiguration.Builder#setBands(int[])} or * {@link SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)}). *
    * * Note: When multiple Soft AP instances are configured, one of the Soft APs may * be shut down independently of the other by the framework. This can happen if no devices * are connected to it for some duration. In that case, one information element will be * returned. * * See {@link #isBridgedApConcurrencySupported()} for support info of multiple (bridged) AP. * * @param softApInfoList is the list of the Soft AP information elements - * {@link SoftApInfo}. */ default void onInfoChanged(@NonNull List softApInfoList) { // Do nothing: can be updated to add SoftApInfo details (e.g. channel) to the UI. } /** * Called when capability of Soft AP changes. * * @param softApCapability is the Soft AP capability. {@link SoftApCapability} */ default void onCapabilityChanged(@NonNull SoftApCapability softApCapability) { // Do nothing: can be updated to add SoftApCapability details (e.g. meximum supported // client number) to the UI. } /** * Called when client trying to connect but device blocked the client with specific reason. * * Can be used to ask user to update client to allowed list or blocked list * when reason is {@link SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER}, or * indicate the block due to maximum supported client number limitation when reason is * {@link SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS}. * * @param client the currently blocked client. * @param blockedReason one of blocked reason from {@link SapClientBlockedReason} */ default void onBlockedClientConnecting(@NonNull WifiClient client, @SapClientBlockedReason int blockedReason) { // Do nothing: can be used to ask user to update client to allowed list or blocked list. } } /** * Callback proxy for SoftApCallback objects. * * @hide */ private class SoftApCallbackProxy extends ISoftApCallback.Stub { private final Executor mExecutor; private final SoftApCallback mCallback; // Either {@link #IFACE_IP_MODE_TETHERED} or {@link #IFACE_IP_MODE_LOCAL_ONLY}. private final int mIpMode; private Map> mCurrentClients = new HashMap<>(); private Map mCurrentInfos = new HashMap<>(); private List getConnectedClientList(Map> clientsMap) { List connectedClientList = new ArrayList<>(); for (List it : clientsMap.values()) { connectedClientList.addAll(it); } return connectedClientList; } SoftApCallbackProxy(Executor executor, SoftApCallback callback, int mode) { mExecutor = executor; mCallback = callback; mIpMode = mode; } @Override public void onStateChanged(SoftApState state) { if (mVerboseLoggingEnabled) { Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", onStateChanged: " + state); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onStateChanged(state); }); } @Override public void onConnectedClientsOrInfoChanged(Map infos, Map> clients, boolean isBridged, boolean isRegistration) { if (mVerboseLoggingEnabled) { Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", onConnectedClientsOrInfoChanged: clients: " + clients + ", infos: " + infos + ", isBridged is " + isBridged + ", isRegistration is " + isRegistration); } List changedInfoList = new ArrayList<>(infos.values()); Map> changedInfoClients = new HashMap<>(); // Some devices may not support infos callback, allow them to support client // connection changed callback. boolean areClientsChangedWithoutInfosChanged = infos.size() == 0 && getConnectedClientList(clients).size() != getConnectedClientList(mCurrentClients).size(); boolean isInfoChanged = infos.size() != mCurrentInfos.size(); if (isRegistration) { // Check if there are clients connected, put it to changedInfoClients for (SoftApInfo currentInfo : infos.values()) { String instance = currentInfo.getApInstanceIdentifier(); if (clients.getOrDefault(instance, Collections.emptyList()).size() > 0) { changedInfoClients.put(currentInfo, clients.get(instance)); } } } // Check if old info removed or not (client changed case) for (SoftApInfo info : mCurrentInfos.values()) { String changedInstance = info.getApInstanceIdentifier(); List changedClientList = clients.getOrDefault( changedInstance, Collections.emptyList()); if (!changedInfoList.contains(info)) { isInfoChanged = true; if (mCurrentClients.getOrDefault(changedInstance, Collections.emptyList()).size() > 0) { SoftApInfo changedInfo = infos.get(changedInstance); if (changedInfo == null || changedInfo.getFrequency() == 0) { Log.d(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", info changed on client connected instance(AP disabled)"); // Send old info with empty client list for shutdown case changedInfoClients.put(info, Collections.emptyList()); } else { Log.d(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", info changed on client connected instance"); changedInfoClients.put(changedInfo, changedClientList); } } } else { // info doesn't change, check client list if (changedClientList.size() != mCurrentClients .getOrDefault(changedInstance, Collections.emptyList()).size()) { // Here should notify client changed on new info(same as old info) changedInfoClients.put(info, changedClientList); } } } mCurrentClients = clients; mCurrentInfos = infos; if (!isInfoChanged && changedInfoClients.isEmpty() && !isRegistration && !areClientsChangedWithoutInfosChanged) { Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", No changed & Not Registration don't need to notify the client"); return; } Binder.clearCallingIdentity(); // Notify the clients changed first for old info shutdown case for (SoftApInfo changedInfo : changedInfoClients.keySet()) { Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", send onConnectedClientsChanged, changedInfo is " + changedInfo + " and clients are " + changedInfoClients.get(changedInfo)); mExecutor.execute(() -> { mCallback.onConnectedClientsChanged( changedInfo, changedInfoClients.get(changedInfo)); }); } if (isInfoChanged || isRegistration) { if (!isBridged) { SoftApInfo newInfo = changedInfoList.isEmpty() ? new SoftApInfo() : changedInfoList.get(0); Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", send InfoChanged, newInfo: " + newInfo); mExecutor.execute(() -> { mCallback.onInfoChanged(newInfo); }); } Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", send InfoChanged, changedInfoList: " + changedInfoList); mExecutor.execute(() -> { mCallback.onInfoChanged(changedInfoList); }); } if (isRegistration || !changedInfoClients.isEmpty() || areClientsChangedWithoutInfosChanged) { Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", send onConnectedClientsChanged(clients): " + getConnectedClientList(clients)); mExecutor.execute(() -> { mCallback.onConnectedClientsChanged(getConnectedClientList(clients)); }); } } @Override public void onCapabilityChanged(SoftApCapability capability) { if (mVerboseLoggingEnabled) { Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", onCapabilityChanged: SoftApCapability = " + capability); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onCapabilityChanged(capability); }); } @Override public void onBlockedClientConnecting(@NonNull WifiClient client, int blockedReason) { if (mVerboseLoggingEnabled) { Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + ", onBlockedClientConnecting: client =" + client + " with reason = " + blockedReason); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onBlockedClientConnecting(client, blockedReason); }); } } /** * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the * following callbacks on registration: *
      *
    • {@link SoftApCallback#onStateChanged(int, int)}
    • *
    • {@link SoftApCallback#onConnectedClientsChanged(List)}
    • *
    • {@link SoftApCallback#onInfoChanged(SoftApInfo)}
    • *
    • {@link SoftApCallback#onInfoChanged(List)}
    • *
    • {@link SoftApCallback#onCapabilityChanged(SoftApCapability)}
    • *
    * * Use {@link SoftApCallback#onConnectedClientsChanged(SoftApInfo, List)} to know * if there are any clients connected to a specific bridged instance of this AP * (if bridged AP is enabled). * * Note: Caller will receive the callback * {@link SoftApCallback#onConnectedClientsChanged(SoftApInfo, List)} * on registration when there are clients connected to AP. * * These will be dispatched on registration to provide the caller with the current state * (and are not an indication of any current change). Note that receiving an immediate * WIFI_AP_STATE_FAILED value for soft AP state indicates that the latest attempt to start * soft AP has failed. Caller can unregister a previously registered callback using * {@link #unregisterSoftApCallback} *

    * Applications should have the * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers * without the permission will trigger a {@link java.lang.SecurityException}. *

    * * @param executor The Executor on whose thread to execute the callbacks of the {@code callback} * object. * @param callback Callback for soft AP events * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public void registerSoftApCallback(@NonNull @CallbackExecutor Executor executor, @NonNull SoftApCallback callback) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", executor=" + executor); try { synchronized (sSoftApCallbackMap) { ISoftApCallback.Stub binderCallback = new SoftApCallbackProxy(executor, callback, IFACE_IP_MODE_TETHERED); sSoftApCallbackMap.put(System.identityHashCode(callback), binderCallback); mService.registerSoftApCallback(binderCallback); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to unregister a previously registered callback. After calling this method, * applications will no longer receive soft AP events. * * @param callback Callback to unregister for soft AP events * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public void unregisterSoftApCallback(@NonNull SoftApCallback callback) { if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "unregisterSoftApCallback: callback=" + callback); try { synchronized (sSoftApCallbackMap) { int callbackIdentifier = System.identityHashCode(callback); if (!sSoftApCallbackMap.contains(callbackIdentifier)) { Log.w(TAG, "Unknown external callback " + callbackIdentifier); return; } mService.unregisterSoftApCallback(sSoftApCallbackMap.get(callbackIdentifier)); sSoftApCallbackMap.remove(callbackIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * LocalOnlyHotspotReservation that contains the {@link SoftApConfiguration} for the active * LocalOnlyHotspot request. *

    * Applications requesting LocalOnlyHotspot for sharing will receive an instance of the * LocalOnlyHotspotReservation in the * {@link LocalOnlyHotspotCallback#onStarted(LocalOnlyHotspotReservation)} call. This * reservation contains the relevant {@link SoftApConfiguration}. * When an application is done with the LocalOnlyHotspot, they should call {@link * LocalOnlyHotspotReservation#close()}. Once this happens, the application will not receive * any further callbacks. If the LocalOnlyHotspot is stopped due to a * user triggered mode change, applications will be notified via the {@link * LocalOnlyHotspotCallback#onStopped()} callback. */ public class LocalOnlyHotspotReservation implements AutoCloseable { private final CloseGuard mCloseGuard = new CloseGuard(); private final SoftApConfiguration mSoftApConfig; private final WifiConfiguration mWifiConfig; private boolean mClosed = false; /** @hide */ @VisibleForTesting public LocalOnlyHotspotReservation(SoftApConfiguration config) { mSoftApConfig = config; mWifiConfig = config.toWifiConfiguration(); mCloseGuard.open("close"); } /** * Returns the {@link WifiConfiguration} of the current Local Only Hotspot (LOHS). * May be null if hotspot enabled and security type is not * {@code WifiConfiguration.KeyMgmt.None} or {@code WifiConfiguration.KeyMgmt.WPA2_PSK}. * * @deprecated Use {@code WifiManager#getSoftApConfiguration()} to get the * LOHS configuration. */ @Deprecated @Nullable public WifiConfiguration getWifiConfiguration() { return mWifiConfig; } /** * Returns the {@link SoftApConfiguration} of the current Local Only Hotspot (LOHS). */ @NonNull public SoftApConfiguration getSoftApConfiguration() { return mSoftApConfig; } @Override public void close() { try { synchronized (mLock) { if (!mClosed) { mClosed = true; stopLocalOnlyHotspot(); mCloseGuard.close(); } } } catch (Exception e) { Log.e(TAG, "Failed to stop Local Only Hotspot."); } finally { Reference.reachabilityFence(this); } } @Override protected void finalize() throws Throwable { try { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } close(); } finally { super.finalize(); } } } /** * Callback class for applications to receive updates about the LocalOnlyHotspot status. */ public static class LocalOnlyHotspotCallback { /** @hide */ public static final int REQUEST_REGISTERED = 0; public static final int ERROR_NO_CHANNEL = 1; public static final int ERROR_GENERIC = 2; public static final int ERROR_INCOMPATIBLE_MODE = 3; public static final int ERROR_TETHERING_DISALLOWED = 4; /** LocalOnlyHotspot start succeeded. */ public void onStarted(LocalOnlyHotspotReservation reservation) {}; /** * LocalOnlyHotspot stopped. *

    * The LocalOnlyHotspot can be disabled at any time by the user. When this happens, * applications will be notified that it was stopped. This will not be invoked when an * application calls {@link LocalOnlyHotspotReservation#close()}. */ public void onStopped() {}; /** * LocalOnlyHotspot failed to start. *

    * Applications can attempt to call * {@link WifiManager#startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)} again at * a later time. *

    * @param reason The reason for failure could be one of: {@link * #ERROR_TETHERING_DISALLOWED}, {@link #ERROR_INCOMPATIBLE_MODE}, * {@link #ERROR_NO_CHANNEL}, or {@link #ERROR_GENERIC}. */ public void onFailed(int reason) { }; } /** * Callback proxy for LocalOnlyHotspotCallback objects. */ private static class LocalOnlyHotspotCallbackProxy extends ILocalOnlyHotspotCallback.Stub { private final WeakReference mWifiManager; private final Executor mExecutor; private final LocalOnlyHotspotCallback mCallback; /** * Constructs a {@link LocalOnlyHotspotCallbackProxy} using the specified executor. All * callbacks will run using the given executor. * * @param manager WifiManager * @param executor Executor for delivering callbacks. * @param callback LocalOnlyHotspotCallback to notify the calling application, or null. */ LocalOnlyHotspotCallbackProxy( @NonNull WifiManager manager, @NonNull @CallbackExecutor Executor executor, @Nullable LocalOnlyHotspotCallback callback) { mWifiManager = new WeakReference<>(manager); mExecutor = executor; mCallback = callback; } @Override public void onHotspotStarted(SoftApConfiguration config) { WifiManager manager = mWifiManager.get(); if (manager == null) return; if (config == null) { Log.e(TAG, "LocalOnlyHotspotCallbackProxy: config cannot be null."); onHotspotFailed(LocalOnlyHotspotCallback.ERROR_GENERIC); return; } final LocalOnlyHotspotReservation reservation = manager.new LocalOnlyHotspotReservation(config); if (mCallback == null) return; mExecutor.execute(() -> mCallback.onStarted(reservation)); } @Override public void onHotspotStopped() { WifiManager manager = mWifiManager.get(); if (manager == null) return; Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped"); if (mCallback == null) return; mExecutor.execute(() -> mCallback.onStopped()); } @Override public void onHotspotFailed(int reason) { WifiManager manager = mWifiManager.get(); if (manager == null) return; Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: " + reason); if (mCallback == null) return; mExecutor.execute(() -> mCallback.onFailed(reason)); } } /** * LocalOnlyHotspotSubscription that is an AutoCloseable object for tracking applications * watching for LocalOnlyHotspot changes. * * @hide */ public class LocalOnlyHotspotSubscription implements AutoCloseable { private final CloseGuard mCloseGuard = new CloseGuard(); /** @hide */ @VisibleForTesting public LocalOnlyHotspotSubscription() { mCloseGuard.open("close"); } @Override public void close() { try { unregisterLocalOnlyHotspotObserver(); mCloseGuard.close(); } catch (Exception e) { Log.e(TAG, "Failed to unregister LocalOnlyHotspotObserver."); } finally { Reference.reachabilityFence(this); } } @Override protected void finalize() throws Throwable { try { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } close(); } finally { super.finalize(); } } } /** * Class to notify calling applications that watch for changes in LocalOnlyHotspot of updates. * * @hide */ public static class LocalOnlyHotspotObserver { /** * Confirm registration for LocalOnlyHotspotChanges by returning a * LocalOnlyHotspotSubscription. */ public void onRegistered(LocalOnlyHotspotSubscription subscription) {}; /** * LocalOnlyHotspot started with the supplied config. */ public void onStarted(SoftApConfiguration config) {}; /** * LocalOnlyHotspot stopped. */ public void onStopped() {}; } /** * Callback proxy for LocalOnlyHotspotObserver objects. */ private static class LocalOnlyHotspotObserverProxy extends ILocalOnlyHotspotCallback.Stub { private final WeakReference mWifiManager; private final Executor mExecutor; private final LocalOnlyHotspotObserver mObserver; /** * Constructs a {@link LocalOnlyHotspotObserverProxy} using the specified looper. * All callbacks will be delivered on the thread of the specified looper. * * @param manager WifiManager * @param executor Executor for delivering callbacks * @param observer LocalOnlyHotspotObserver to notify the calling application. */ LocalOnlyHotspotObserverProxy(WifiManager manager, Executor executor, final LocalOnlyHotspotObserver observer) { mWifiManager = new WeakReference<>(manager); mExecutor = executor; mObserver = observer; } public void registered() throws RemoteException { WifiManager manager = mWifiManager.get(); if (manager == null) return; mExecutor.execute(() -> mObserver.onRegistered(manager.new LocalOnlyHotspotSubscription())); } @Override public void onHotspotStarted(SoftApConfiguration config) { WifiManager manager = mWifiManager.get(); if (manager == null) return; if (config == null) { Log.e(TAG, "LocalOnlyHotspotObserverProxy: config cannot be null."); return; } mExecutor.execute(() -> mObserver.onStarted(config)); } @Override public void onHotspotStopped() { WifiManager manager = mWifiManager.get(); if (manager == null) return; mExecutor.execute(() -> mObserver.onStopped()); } @Override public void onHotspotFailed(int reason) { // do nothing } } /** * Callback proxy for ActionListener objects. */ private class ActionListenerProxy extends IActionListener.Stub { private final String mActionTag; private final Handler mHandler; private final ActionListener mCallback; ActionListenerProxy(String actionTag, Looper looper, ActionListener callback) { mActionTag = actionTag; mHandler = new Handler(looper); mCallback = callback; } @Override public void onSuccess() { if (mVerboseLoggingEnabled) { Log.v(TAG, "ActionListenerProxy:" + mActionTag + ": onSuccess"); } mHandler.post(() -> { mCallback.onSuccess(); }); } @Override public void onFailure(@ActionListenerFailureReason int reason) { if (mVerboseLoggingEnabled) { Log.v(TAG, "ActionListenerProxy:" + mActionTag + ": onFailure=" + reason); } mHandler.post(() -> { mCallback.onFailure(reason); }); } } private void connectInternal(@Nullable WifiConfiguration config, int networkId, @Nullable ActionListener listener) { ActionListenerProxy listenerProxy = null; if (listener != null) { listenerProxy = new ActionListenerProxy("connect", mLooper, listener); } try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.connect(config, networkId, listenerProxy, mContext.getOpPackageName(), extras); } catch (RemoteException e) { if (listenerProxy != null) { listenerProxy.onFailure(ActionListener.FAILURE_INTERNAL_ERROR); } } catch (SecurityException e) { if (listenerProxy != null) { listenerProxy.onFailure(ActionListener.FAILURE_NOT_AUTHORIZED); } } } /** * Connect to a network with the given configuration. The network also * gets added to the list of configured networks for the foreground user. * * For a new network, this function is used instead of a * sequence of addNetwork(), enableNetwork(), and reconnect() * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK }) public void connect(@NonNull WifiConfiguration config, @Nullable ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); connectInternal(config, WifiConfiguration.INVALID_NETWORK_ID, listener); } /** * Connect to a network with the given networkId. * * This function is used instead of a enableNetwork() and reconnect() * *

  • This API will cause reconnect if the credentials of the current active * connection has been changed.
  • *
  • This API will cause reconnect if the current active connection is marked metered.
  • * * @param networkId the ID of the network as returned by {@link #addNetwork} or {@link * #getConfiguredNetworks()}. * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK }) public void connect(int networkId, @Nullable ActionListener listener) { if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative"); connectInternal(null, networkId, listener); } /** * Temporarily disable autojoin for all currently visible and provisioned (saved, suggested) * wifi networks except merged carrier networks from the provided subscription ID. * * Disabled networks will get automatically re-enabled when they are out of range for a period * of time, or after the maximum disable duration specified in the framework. * * Calling {@link #stopRestrictingAutoJoinToSubscriptionId()} will immediately re-enable * autojoin on all disabled networks. * * @param subscriptionId the subscription ID of the carrier whose merged wifi networks won't be * disabled {@link android.telephony.SubscriptionInfo#getSubscriptionId()} * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) @RequiresApi(Build.VERSION_CODES.S) public void startRestrictingAutoJoinToSubscriptionId(int subscriptionId) { try { mService.startRestrictingAutoJoinToSubscriptionId(subscriptionId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Re-enable autojoin for all non carrier merged wifi networks temporarily disconnected by * {@link #startRestrictingAutoJoinToSubscriptionId(int)}. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) @RequiresApi(Build.VERSION_CODES.S) public void stopRestrictingAutoJoinToSubscriptionId() { try { mService.stopRestrictingAutoJoinToSubscriptionId(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Save the given network to the list of configured networks for the * foreground user. If the network already exists, the configuration * is updated. Any new network is enabled by default. * * For a new network, this function is used instead of a * sequence of addNetwork() and enableNetwork(). * * For an existing network, it accomplishes the task of updateNetwork() * *
  • This API will cause reconnect if the credentials of the current active * connection has been changed.
  • *
  • This API will cause disconnect if the current active connection is marked metered.
  • * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK }) public void save(@NonNull WifiConfiguration config, @Nullable ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); ActionListenerProxy listenerProxy = null; if (listener != null) { listenerProxy = new ActionListenerProxy("save", mLooper, listener); } try { mService.save(config, listenerProxy, mContext.getOpPackageName()); } catch (RemoteException e) { if (listenerProxy != null) { listenerProxy.onFailure(ActionListener.FAILURE_INTERNAL_ERROR); } } catch (SecurityException e) { if (listenerProxy != null) { listenerProxy.onFailure(ActionListener.FAILURE_NOT_AUTHORIZED); } } } /** * Delete the network from the list of configured networks for the * foreground user. * * This function is used instead of a sequence of removeNetwork() * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK }) public void forget(int netId, @Nullable ActionListener listener) { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); ActionListenerProxy listenerProxy = null; if (listener != null) { listenerProxy = new ActionListenerProxy("forget", mLooper, listener); } try { mService.forget(netId, listenerProxy); } catch (RemoteException e) { if (listenerProxy != null) { listenerProxy.onFailure(ActionListener.FAILURE_INTERNAL_ERROR); } } catch (SecurityException e) { if (listenerProxy != null) { listenerProxy.onFailure(ActionListener.FAILURE_NOT_AUTHORIZED); } } } /** * Disable network * * @param netId is the network Id * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again * @deprecated This API is deprecated. Use {@link #disableNetwork(int)} instead. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK }) @Deprecated public void disable(int netId, @Nullable ActionListener listener) { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); // Simple wrapper which forwards the call to disableNetwork. This is a temporary // implementation until we can remove this API completely. boolean status = disableNetwork(netId); if (listener != null) { if (status) { listener.onSuccess(); } else { listener.onFailure(ActionListener.FAILURE_INTERNAL_ERROR); } } } /** * Control whether the device will automatically search for and connect to Wi-Fi networks - * auto-join Wi-Fi networks. Disabling this option will not impact manual connections - i.e. * the user will still be able to manually select and connect to a Wi-Fi network. Disabling * this option significantly impacts the device connectivity and is a restricted operation * (see below for permissions). Note that disabling this operation will also disable * connectivity initiated scanning operations. *

    * Disabling the auto-join configuration is a temporary operation (with the exception of a * DO/PO caller): it will be reset (to enabled) when the device reboots or the user toggles * Wi-Fi off/on. When the caller is a DO/PO then toggling Wi-Fi will not reset the * configuration. Additionally, if a DO/PO disables auto-join then it cannot be (re)enabled by * a non-DO/PO caller. * * @param allowAutojoin true to allow auto-join, false to disallow auto-join * * Available for DO/PO apps. * Other apps require {@code android.Manifest.permission#NETWORK_SETTINGS} or * {@code android.Manifest.permission#MANAGE_WIFI_NETWORK_SELECTION} permission. */ public void allowAutojoinGlobal(boolean allowAutojoin) { try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.allowAutojoinGlobal(allowAutojoin, mContext.getOpPackageName(), extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Query whether or not auto-join global is enabled/disabled * @see #allowAutojoinGlobal(boolean) * * Available for DO/PO apps. * Other apps require {@code android.Manifest.permission#NETWORK_SETTINGS} or * {@code android.Manifest.permission#MANAGE_WIFI_NETWORK_SELECTION} permission. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return {@code Boolean} indicating * whether auto-join global is enabled/disabled. * * @throws SecurityException if the caller does not have permission. * @throws NullPointerException if the caller provided invalid inputs. */ public void queryAutojoinGlobal(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.queryAutojoinGlobal( new IBooleanListener.Stub() { @Override public void onResult(boolean value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Sets the user choice for allowing auto-join to a network. * The updated choice will be made available through the updated config supplied by the * CONFIGURED_NETWORKS_CHANGED broadcast. * * @param netId the id of the network to allow/disallow auto-join for. * @param allowAutojoin true to allow auto-join, false to disallow auto-join * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int netId, boolean allowAutojoin) { try { mService.allowAutojoin(netId, allowAutojoin); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Configure auto-join settings for a Passpoint profile. * * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile. * @param allowAutojoin true to enable auto-join, false to disable auto-join. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String fqdn, boolean allowAutojoin) { try { mService.allowAutojoinPasspoint(fqdn, allowAutojoin); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Configure MAC randomization setting for a Passpoint profile. * MAC randomization is enabled by default. * * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile. * @param enable true to enable MAC randomization, false to disable MAC randomization. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String fqdn, boolean enable) { try { mService.setMacRandomizationSettingPasspointEnabled(fqdn, enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Sets the user's choice of metered override for a Passpoint profile. * * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile. * @param meteredOverride One of three values: {@link WifiConfiguration#METERED_OVERRIDE_NONE}, * {@link WifiConfiguration#METERED_OVERRIDE_METERED}, * {@link WifiConfiguration#METERED_OVERRIDE_NOT_METERED} * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setPasspointMeteredOverride(@NonNull String fqdn, @WifiConfiguration.MeteredOverride int meteredOverride) { try { mService.setPasspointMeteredOverride(fqdn, meteredOverride); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Temporarily disable a network. Should always trigger with user disconnect network. * * @param network Input can be SSID or FQDN. And caller must ensure that the SSID passed thru * this API matched the WifiConfiguration.SSID rules, and thus be surrounded by * quotes. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK }) public void disableEphemeralNetwork(@NonNull String network) { if (TextUtils.isEmpty(network)) { throw new IllegalArgumentException("SSID cannot be null or empty!"); } try { mService.disableEphemeralNetwork(network, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * WPS suport has been deprecated from Client mode and this method will immediately trigger * {@link WpsCallback#onFailed(int)} with a generic error. * * @param config WPS configuration (does not support {@link WpsInfo#LABEL}) * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be initialized again * @deprecated This API is deprecated */ public void startWps(WpsInfo config, WpsCallback listener) { if (listener != null ) { listener.onFailed(ActionListener.FAILURE_INTERNAL_ERROR); } } /** * WPS support has been deprecated from Client mode and this method will immediately trigger * {@link WpsCallback#onFailed(int)} with a generic error. * * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be initialized again * @deprecated This API is deprecated */ public void cancelWps(WpsCallback listener) { if (listener != null) { listener.onFailed(ActionListener.FAILURE_INTERNAL_ERROR); } } /** * Allows an application to keep the Wi-Fi radio awake. * Normally the Wi-Fi radio may turn off when the user has not used the device in a while. * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple * applications may hold WifiLocks, and the radio will only be allowed to turn off when no * WifiLocks are held in any application. *

    * Before using a WifiLock, consider carefully if your application requires Wi-Fi access, or * could function over a mobile network, if available. A program that needs to download large * files should hold a WifiLock to ensure that the download will complete, but a program whose * network usage is occasional or low-bandwidth should not hold a WifiLock to avoid adversely * affecting battery life. *

    * Note that WifiLocks cannot override the user-level "Wi-Fi Enabled" setting, nor Airplane * Mode. They simply keep the radio from turning off when Wi-Fi is already on but the device * is idle. *

    * Any application using a WifiLock must request the {@code android.permission.WAKE_LOCK} * permission in an {@code } element of the application's manifest. */ public class WifiLock { private String mTag; private final IBinder mBinder; private int mRefCount; int mLockType; private boolean mRefCounted; private boolean mHeld; private WorkSource mWorkSource; private WifiLock(int lockType, String tag) { mTag = tag; mLockType = lockType; mBinder = new Binder(); mRefCount = 0; mRefCounted = true; mHeld = false; } /** * Locks the Wi-Fi radio on until {@link #release} is called. * * If this WifiLock is reference-counted, each call to {@code acquire} will increment the * reference count, and the radio will remain locked as long as the reference count is * above zero. * * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock * the radio, but subsequent calls will be ignored. Only one call to {@link #release} * will be required, regardless of the number of times that {@code acquire} is called. */ public void acquire() { synchronized (mBinder) { if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) { try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource, mContext.getOpPackageName(), extras); synchronized (WifiManager.this) { if (mActiveLockCount >= MAX_ACTIVE_LOCKS) { mService.releaseWifiLock(mBinder); throw new UnsupportedOperationException( "Exceeded maximum number of wifi locks"); } mActiveLockCount++; } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mHeld = true; } } } /** * Unlocks the Wi-Fi radio, allowing it to turn off when the device is idle. * * If this WifiLock is reference-counted, each call to {@code release} will decrement the * reference count, and the radio will be unlocked only when the reference count reaches * zero. If the reference count goes below zero (that is, if {@code release} is called * a greater number of times than {@link #acquire}), an exception is thrown. * * If this WifiLock is not reference-counted, the first call to {@code release} (after * the radio was locked using {@link #acquire}) will unlock the radio, and subsequent * calls will be ignored. */ public void release() { synchronized (mBinder) { if (mRefCounted ? (--mRefCount == 0) : (mHeld)) { try { mService.releaseWifiLock(mBinder); synchronized (WifiManager.this) { mActiveLockCount--; } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mHeld = false; } if (mRefCount < 0) { throw new RuntimeException("WifiLock under-locked " + mTag); } } } /** * Controls whether this is a reference-counted or non-reference-counted WifiLock. * * Reference-counted WifiLocks keep track of the number of calls to {@link #acquire} and * {@link #release}, and only allow the radio to sleep when every call to {@link #acquire} * has been balanced with a call to {@link #release}. Non-reference-counted WifiLocks * lock the radio whenever {@link #acquire} is called and it is unlocked, and unlock the * radio whenever {@link #release} is called and it is locked. * * @param refCounted true if this WifiLock should keep a reference count */ public void setReferenceCounted(boolean refCounted) { mRefCounted = refCounted; } /** * Checks whether this WifiLock is currently held. * * @return true if this WifiLock is held, false otherwise */ public boolean isHeld() { synchronized (mBinder) { return mHeld; } } public void setWorkSource(WorkSource ws) { synchronized (mBinder) { if (ws != null && ws.isEmpty()) { ws = null; } boolean changed = true; if (ws == null) { mWorkSource = null; } else { ws = ws.withoutNames(); if (mWorkSource == null) { changed = mWorkSource != null; mWorkSource = new WorkSource(ws); } else { changed = !mWorkSource.equals(ws); if (changed) { mWorkSource.set(ws); } } } if (changed && mHeld) { try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.updateWifiLockWorkSource(mBinder, mWorkSource, mContext.getOpPackageName(), extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } } public String toString() { String s1, s2, s3; synchronized (mBinder) { s1 = Integer.toHexString(System.identityHashCode(this)); s2 = mHeld ? "held; " : ""; if (mRefCounted) { s3 = "refcounted: refcount = " + mRefCount; } else { s3 = "not refcounted"; } return "WifiLock{ " + s1 + "; " + s2 + s3 + " }"; } } @Override protected void finalize() throws Throwable { super.finalize(); synchronized (mBinder) { if (mHeld) { try { mService.releaseWifiLock(mBinder); synchronized (WifiManager.this) { mActiveLockCount--; } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } } } /** * Creates a new WifiLock. * * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL_HIGH_PERF} * and {@link #WIFI_MODE_FULL_LOW_LATENCY} for descriptions of the types of Wi-Fi locks. * @param tag a tag for the WifiLock to identify it in debugging messages. This string is * never shown to the user under normal conditions, but should be descriptive * enough to identify your application and the specific WifiLock within it, if it * holds multiple WifiLocks. * * @return a new, unacquired WifiLock with the given tag. * * @see WifiLock */ public WifiLock createWifiLock(int lockType, String tag) { return new WifiLock(lockType, tag); } /** * Interface for low latency lock listener. Should be extended by application and set when * calling {@link WifiManager#addWifiLowLatencyLockListener(Executor, * WifiLowLatencyLockListener)}. * * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public interface WifiLowLatencyLockListener { /** * Provides low latency mode is activated or not. Triggered when Wi-Fi chip enters into low * latency mode. * *

    Note: Always called with current state when a new listener gets registered. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) void onActivatedStateChanged(boolean activated); /** * Provides UIDs (lock owners) of the applications which currently acquired low latency * lock. Triggered when an application acquires or releases a lock. * *

    Note: Always called with UIDs of the current acquired locks when a new listener gets * registered. * * @param ownerUids An array of UIDs. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onOwnershipChanged(@NonNull int[] ownerUids) {} /** * Provides UIDs of the applications which acquired the low latency lock and is currently * active. See {@link WifiManager#WIFI_MODE_FULL_LOW_LATENCY} for the conditions to be met * for low latency lock to be active. Triggered when application acquiring the lock * satisfies or does not satisfy low latency conditions when the low latency mode is * activated. Also gets triggered when the lock becomes active, immediately after the {@link * WifiLowLatencyLockListener#onActivatedStateChanged(boolean)} callback is triggered. * *

    Note: Always called with UIDs of the current active locks when a new listener gets * registered if the Wi-Fi chip is in low latency mode. * * @param activeUids An array of UIDs. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onActiveUsersChanged(@NonNull int[] activeUids) {} } /** * Helper class to support wifi low latency lock listener. */ private static class OnWifiLowLatencyLockProxy extends IWifiLowLatencyLockListener.Stub { @NonNull private Executor mExecutor; @NonNull private WifiLowLatencyLockListener mListener; OnWifiLowLatencyLockProxy(@NonNull Executor executor, @NonNull WifiLowLatencyLockListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); mExecutor = executor; mListener = listener; } @Override public void onActivatedStateChanged(boolean activated) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onActivatedStateChanged(activated)); } @Override public void onOwnershipChanged(@NonNull int[] ownerUids) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onOwnershipChanged(ownerUids)); } @Override public void onActiveUsersChanged(@NonNull int[] activeUids) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onActiveUsersChanged(activeUids)); } } /** * Add a listener for monitoring the low latency lock. The caller can unregister a previously * registered listener using {@link WifiManager#removeWifiLowLatencyLockListener( * WifiLowLatencyLockListener)}. * *

    Applications should have the {@link android.Manifest.permission#NETWORK_SETTINGS} and * {@link android.Manifest.permission#MANAGE_WIFI_NETWORK_SELECTION} permission. Callers without * the permission will trigger a {@link java.lang.SecurityException}. * * @param executor The Executor on which to execute the callbacks. * @param listener The listener for the latency mode change. * @throws IllegalArgumentException if incorrect input arguments are provided. * @throws SecurityException if the caller is not allowed to call this API * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission( anyOf = {android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION}) public void addWifiLowLatencyLockListener( @NonNull @CallbackExecutor Executor executor, @NonNull WifiLowLatencyLockListener listener) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (listener == null) throw new IllegalArgumentException("listener cannot be null"); if (mVerboseLoggingEnabled) { Log.d(TAG, "addWifiLowLatencyLockListener: listener=" + listener + ", executor=" + executor); } final int listenerIdentifier = System.identityHashCode(listener); try { synchronized (sWifiLowLatencyLockListenerMap) { IWifiLowLatencyLockListener.Stub listenerProxy = new OnWifiLowLatencyLockProxy( executor, listener); sWifiLowLatencyLockListenerMap.put(listenerIdentifier, listenerProxy); mService.addWifiLowLatencyLockListener(listenerProxy); } } catch (RemoteException e) { sWifiLowLatencyLockListenerMap.remove(listenerIdentifier); throw e.rethrowFromSystemServer(); } } /** * Removes a listener added using {@link WifiManager#addWifiLowLatencyLockListener(Executor, * WifiLowLatencyLockListener)}. After calling this method, applications will no longer receive * low latency mode notifications. * * @param listener the listener to be removed. * @throws IllegalArgumentException if incorrect input arguments are provided. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void removeWifiLowLatencyLockListener(@NonNull WifiLowLatencyLockListener listener) { if (listener == null) throw new IllegalArgumentException("listener cannot be null"); if (mVerboseLoggingEnabled) { Log.d(TAG, "removeWifiLowLatencyLockListener: listener=" + listener); } final int listenerIdentifier = System.identityHashCode(listener); synchronized (sWifiLowLatencyLockListenerMap) { try { if (!sWifiLowLatencyLockListenerMap.contains(listenerIdentifier)) { Log.w(TAG, "Unknown external listener " + listenerIdentifier); return; } mService.removeWifiLowLatencyLockListener( sWifiLowLatencyLockListenerMap.get(listenerIdentifier)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { sWifiLowLatencyLockListenerMap.remove(listenerIdentifier); } } } /** * Creates a new WifiLock. * * @param tag a tag for the WifiLock to identify it in debugging messages. This string is * never shown to the user under normal conditions, but should be descriptive * enough to identify your application and the specific WifiLock within it, if it * holds multiple WifiLocks. * * @return a new, unacquired WifiLock with the given tag. * * @see WifiLock * * @deprecated This API is non-functional. */ @Deprecated public WifiLock createWifiLock(String tag) { return new WifiLock(WIFI_MODE_FULL, tag); } /** * Create a new MulticastLock * * @param tag a tag for the MulticastLock to identify it in debugging * messages. This string is never shown to the user under * normal conditions, but should be descriptive enough to * identify your application and the specific MulticastLock * within it, if it holds multiple MulticastLocks. * * @return a new, unacquired MulticastLock with the given tag. * * @see MulticastLock */ public MulticastLock createMulticastLock(String tag) { return new MulticastLock(tag); } /** * Allows an application to receive Wifi Multicast packets. * Normally the Wifi stack filters out packets not explicitly * addressed to this device. Acquring a MulticastLock will * cause the stack to receive packets addressed to multicast * addresses. Processing these extra packets can cause a noticeable * battery drain and should be disabled when not needed. */ public class MulticastLock { private String mTag; private final IBinder mBinder; private int mRefCount; private boolean mRefCounted; private boolean mHeld; private MulticastLock(String tag) { mTag = tag; mBinder = new Binder(); mRefCount = 0; mRefCounted = true; mHeld = false; } /** * Locks Wifi Multicast on until {@link #release} is called. * * If this MulticastLock is reference-counted each call to * {@code acquire} will increment the reference count, and the * wifi interface will receive multicast packets as long as the * reference count is above zero. * * If this MulticastLock is not reference-counted, the first call to * {@code acquire} will turn on the multicast packets, but subsequent * calls will be ignored. Only one call to {@link #release} will * be required, regardless of the number of times that {@code acquire} * is called. * * Note that other applications may also lock Wifi Multicast on. * Only they can relinquish their lock. * * Also note that applications cannot leave Multicast locked on. * When an app exits or crashes, any Multicast locks will be released. */ public void acquire() { synchronized (mBinder) { if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) { try { mService.acquireMulticastLock(mBinder, mTag); synchronized (WifiManager.this) { if (mActiveLockCount >= MAX_ACTIVE_LOCKS) { mService.releaseMulticastLock(mTag); throw new UnsupportedOperationException( "Exceeded maximum number of wifi locks"); } mActiveLockCount++; } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mHeld = true; } } } /** * Unlocks Wifi Multicast, restoring the filter of packets * not addressed specifically to this device and saving power. * * If this MulticastLock is reference-counted, each call to * {@code release} will decrement the reference count, and the * multicast packets will only stop being received when the reference * count reaches zero. If the reference count goes below zero (that * is, if {@code release} is called a greater number of times than * {@link #acquire}), an exception is thrown. * * If this MulticastLock is not reference-counted, the first call to * {@code release} (after the radio was multicast locked using * {@link #acquire}) will unlock the multicast, and subsequent calls * will be ignored. * * Note that if any other Wifi Multicast Locks are still outstanding * this {@code release} call will not have an immediate effect. Only * when all applications have released all their Multicast Locks will * the Multicast filter be turned back on. * * Also note that when an app exits or crashes all of its Multicast * Locks will be automatically released. */ public void release() { synchronized (mBinder) { if (mRefCounted ? (--mRefCount == 0) : (mHeld)) { try { mService.releaseMulticastLock(mTag); synchronized (WifiManager.this) { mActiveLockCount--; } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mHeld = false; } if (mRefCount < 0) { throw new RuntimeException("MulticastLock under-locked " + mTag); } } } /** * Controls whether this is a reference-counted or non-reference- * counted MulticastLock. * * Reference-counted MulticastLocks keep track of the number of calls * to {@link #acquire} and {@link #release}, and only stop the * reception of multicast packets when every call to {@link #acquire} * has been balanced with a call to {@link #release}. Non-reference- * counted MulticastLocks allow the reception of multicast packets * whenever {@link #acquire} is called and stop accepting multicast * packets whenever {@link #release} is called. * * @param refCounted true if this MulticastLock should keep a reference * count */ public void setReferenceCounted(boolean refCounted) { mRefCounted = refCounted; } /** * Checks whether this MulticastLock is currently held. * * @return true if this MulticastLock is held, false otherwise */ public boolean isHeld() { synchronized (mBinder) { return mHeld; } } public String toString() { String s1, s2, s3; synchronized (mBinder) { s1 = Integer.toHexString(System.identityHashCode(this)); s2 = mHeld ? "held; " : ""; if (mRefCounted) { s3 = "refcounted: refcount = " + mRefCount; } else { s3 = "not refcounted"; } return "MulticastLock{ " + s1 + "; " + s2 + s3 + " }"; } } @Override protected void finalize() throws Throwable { super.finalize(); setReferenceCounted(false); release(); } } /** * Check multicast filter status. * * @return true if multicast packets are allowed. * * @hide pending API council approval */ public boolean isMulticastEnabled() { try { return mService.isMulticastEnabled(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Initialize the multicast filtering to 'on' * @hide no intent to publish */ @UnsupportedAppUsage public boolean initializeMulticastFiltering() { try { mService.initializeMulticastFiltering(); return true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Set Wi-Fi verbose logging level from developer settings. * * @param enable true to enable verbose logging, false to disable. * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.DUMP }) public void setVerboseLoggingEnabled(boolean enable) { enableVerboseLogging(enable ? VERBOSE_LOGGING_LEVEL_ENABLED : VERBOSE_LOGGING_LEVEL_DISABLED); } /** * Set Wi-Fi verbose logging level from developer settings. * * @param verbose the verbose logging mode which could be * {@link #VERBOSE_LOGGING_LEVEL_DISABLED}, {@link #VERBOSE_LOGGING_LEVEL_ENABLED}, or * {@link #VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY}. * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.DUMP }) public void setVerboseLoggingLevel(@VerboseLoggingLevel int verbose) { enableVerboseLogging(verbose); } /** @hide */ @UnsupportedAppUsage( maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@code #setVerboseLoggingEnabled(boolean)} instead." ) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.DUMP }) public void enableVerboseLogging(@VerboseLoggingLevel int verbose) { try { mService.enableVerboseLogging(verbose); mVerboseLoggingEnabled = verbose == VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY || verbose == VERBOSE_LOGGING_LEVEL_ENABLED; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the persisted Wi-Fi verbose logging level, set by * {@link #setVerboseLoggingEnabled(boolean)} or {@link #setVerboseLoggingLevel(int)}. * No permissions are required to call this method. * * @return true to indicate that verbose logging is enabled, false to indicate that verbose * logging is disabled. * * @hide */ @SystemApi public boolean isVerboseLoggingEnabled() { int verboseLoggingLevel = getVerboseLoggingLevel(); return verboseLoggingLevel == VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY || verboseLoggingLevel == VERBOSE_LOGGING_LEVEL_ENABLED; } /** * Get the persisted Wi-Fi verbose logging level, set by * {@link #setVerboseLoggingEnabled(boolean)} or {@link #setVerboseLoggingLevel(int)}. * No permissions are required to call this method. * * @return one of {@link #VERBOSE_LOGGING_LEVEL_DISABLED}, * {@link #VERBOSE_LOGGING_LEVEL_ENABLED}, * or {@link #VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY}. * * @hide */ @SystemApi public @VerboseLoggingLevel int getVerboseLoggingLevel() { try { return mService.getVerboseLoggingLevel(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Removes all saved Wi-Fi networks, Passpoint configurations, ephemeral networks, Network * Requests, and Network Suggestions. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset() { try { mService.factoryReset(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get {@link Network} object of current wifi network, or null if not connected. * @hide */ @Nullable @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) public Network getCurrentNetwork() { try { return mService.getCurrentNetwork(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Deprecated * returns false * @hide * @deprecated */ public boolean setEnableAutoJoinWhenAssociated(boolean enabled) { return false; } /** * Deprecated * returns false * @hide * @deprecated */ public boolean getEnableAutoJoinWhenAssociated() { return false; } /** * Returns a byte stream representing the data that needs to be backed up to save the * current Wifi state. * This Wifi state can be restored by calling {@link #restoreWifiBackupData(byte[])}. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public void retrieveWifiBackupData(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); } Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.retrieveWifiBackupData( new IByteArrayListener.Stub() { @Override public void onResult(byte[] value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Restore state from the backed up data. * @param data byte stream in the same format produced by {@link #retrieveWifiBackupData()} * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public void restoreWifiBackupData(@NonNull byte[] data) { if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); } try { mService.restoreWifiBackupData(data); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a byte stream representing the data that needs to be backed up to save the * current Wifi state. * This Wifi state can be restored by calling {@link #restoreBackupData(byte[])}. * @hide */ @NonNull @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData() { try { return mService.retrieveBackupData(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Restore state from the backed up data. * @param data byte stream in the same format produced by {@link #retrieveBackupData()} * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[] data) { try { mService.restoreBackupData(data); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a byte stream representing the data that needs to be backed up to save the * current soft ap config data. * * This soft ap config can be restored by calling {@link #restoreSoftApBackupData(byte[])} * @hide */ @NonNull @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData() { try { return mService.retrieveSoftApBackupData(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns soft ap config from the backed up data or null if data is invalid. * @param data byte stream in the same format produced by {@link #retrieveSoftApBackupData()} * * @hide */ @Nullable @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public SoftApConfiguration restoreSoftApBackupData(@NonNull byte[] data) { try { return mService.restoreSoftApBackupData(data); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Restore state from the older version of back up data. * The old backup data was essentially a backup of wpa_supplicant.conf * and ipconfig.txt file. * @param supplicantData bytes representing wpa_supplicant.conf * @param ipConfigData bytes representing ipconfig.txt * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSupplicantBackupData( @NonNull byte[] supplicantData, @NonNull byte[] ipConfigData) { try { mService.restoreSupplicantBackupData(supplicantData, ipConfigData); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Start subscription provisioning flow * * @param provider {@link OsuProvider} to provision with * @param executor the Executor on which to run the callback. * @param callback {@link ProvisioningCallback} for updates regarding provisioning flow * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) public void startSubscriptionProvisioning(@NonNull OsuProvider provider, @NonNull @CallbackExecutor Executor executor, @NonNull ProvisioningCallback callback) { // Verify arguments if (executor == null) { throw new IllegalArgumentException("executor must not be null"); } if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } try { mService.startSubscriptionProvisioning(provider, new ProvisioningCallbackProxy(executor, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Helper class to support OSU Provisioning callbacks */ private static class ProvisioningCallbackProxy extends IProvisioningCallback.Stub { private final Executor mExecutor; private final ProvisioningCallback mCallback; ProvisioningCallbackProxy(Executor executor, ProvisioningCallback callback) { mExecutor = executor; mCallback = callback; } @Override public void onProvisioningStatus(int status) { mExecutor.execute(() -> mCallback.onProvisioningStatus(status)); } @Override public void onProvisioningFailure(int status) { mExecutor.execute(() -> mCallback.onProvisioningFailure(status)); } @Override public void onProvisioningComplete() { mExecutor.execute(() -> mCallback.onProvisioningComplete()); } } /** * Interface for Traffic state callback. Should be extended by applications and set when * calling {@link #registerTrafficStateCallback(Executor, WifiManager.TrafficStateCallback)}. * @hide */ @SystemApi public interface TrafficStateCallback { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"DATA_ACTIVITY_"}, value = { DATA_ACTIVITY_NONE, DATA_ACTIVITY_IN, DATA_ACTIVITY_OUT, DATA_ACTIVITY_INOUT}) @interface DataActivity {} // Lowest bit indicates data reception and the second lowest bit indicates data transmitted /** No data in or out */ int DATA_ACTIVITY_NONE = 0x00; /** Data in, no data out */ int DATA_ACTIVITY_IN = 0x01; /** Data out, no data in */ int DATA_ACTIVITY_OUT = 0x02; /** Data in and out */ int DATA_ACTIVITY_INOUT = 0x03; /** * Callback invoked to inform clients about the current traffic state. * * @param state One of the values: {@link #DATA_ACTIVITY_NONE}, {@link #DATA_ACTIVITY_IN}, * {@link #DATA_ACTIVITY_OUT} & {@link #DATA_ACTIVITY_INOUT}. */ void onStateChanged(@DataActivity int state); } /** * Callback proxy for TrafficStateCallback objects. * * @hide */ private class TrafficStateCallbackProxy extends ITrafficStateCallback.Stub { private final Executor mExecutor; private final TrafficStateCallback mCallback; TrafficStateCallbackProxy(Executor executor, TrafficStateCallback callback) { mExecutor = executor; mCallback = callback; } @Override public void onStateChanged(int state) { if (mVerboseLoggingEnabled) { Log.v(TAG, "TrafficStateCallbackProxy: onStateChanged state=" + state); } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mCallback.onStateChanged(state); }); } } /** * Registers a callback for monitoring traffic state. See {@link TrafficStateCallback}. These * callbacks will be invoked periodically by platform to inform clients about the current * traffic state. Caller can unregister a previously registered callback using * {@link #unregisterTrafficStateCallback(TrafficStateCallback)} *

    * Applications should have the * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers * without the permission will trigger a {@link java.lang.SecurityException}. *

    * * @param executor The Executor on whose thread to execute the callbacks of the {@code callback} * object. * @param callback Callback for traffic state events * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull @CallbackExecutor Executor executor, @NonNull TrafficStateCallback callback) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerTrafficStateCallback: callback=" + callback + ", executor=" + executor); try { synchronized (sTrafficStateCallbackMap) { ITrafficStateCallback.Stub binderCallback = new TrafficStateCallbackProxy(executor, callback); sTrafficStateCallbackMap.put(System.identityHashCode(callback), binderCallback); mService.registerTrafficStateCallback(binderCallback); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to unregister a previously registered callback. After calling this method, * applications will no longer receive traffic state notifications. * * @param callback Callback to unregister for traffic state events * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull TrafficStateCallback callback) { if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "unregisterTrafficStateCallback: callback=" + callback); try { synchronized (sTrafficStateCallbackMap) { int callbackIdentifier = System.identityHashCode(callback); if (!sTrafficStateCallbackMap.contains(callbackIdentifier)) { Log.w(TAG, "Unknown external callback " + callbackIdentifier); return; } mService.unregisterTrafficStateCallback( sTrafficStateCallbackMap.get(callbackIdentifier)); sTrafficStateCallbackMap.remove(callbackIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Helper method to update the local verbose logging flag based on the verbose logging * level from wifi service. */ private void updateVerboseLoggingEnabledFromService() { mVerboseLoggingEnabled = isVerboseLoggingEnabled(); } /** * @return true if this device supports WPA3-Personal SAE */ public boolean isWpa3SaeSupported() { return isFeatureSupported(WIFI_FEATURE_WPA3_SAE); } /** * @return true if this device supports WPA3-Enterprise Suite-B-192 */ public boolean isWpa3SuiteBSupported() { return isFeatureSupported(WIFI_FEATURE_WPA3_SUITE_B); } /** * @return true if this device supports Wi-Fi Enhanced Open (OWE) */ public boolean isEnhancedOpenSupported() { return isFeatureSupported(WIFI_FEATURE_OWE); } /** * Wi-Fi Easy Connect (DPP) introduces standardized mechanisms to simplify the provisioning and * configuration of Wi-Fi devices. * For more details, visit https://www.wi-fi.org/ and * search for "Easy Connect" or "Device Provisioning Protocol specification". * * @return true if this device supports Wi-Fi Easy-connect (Device Provisioning Protocol) */ public boolean isEasyConnectSupported() { return isFeatureSupported(WIFI_FEATURE_DPP); } /** * @return true if this device supports Wi-Fi Easy Connect (DPP) Enrollee Responder mode. */ public boolean isEasyConnectEnrolleeResponderModeSupported() { return isFeatureSupported(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER); } /** * @return true if this device supports WAPI. */ public boolean isWapiSupported() { return isFeatureSupported(WIFI_FEATURE_WAPI); } /** * @return true if this device supports WPA3 SAE Public Key. */ public boolean isWpa3SaePublicKeySupported() { // This feature is not fully implemented in the framework yet. // After the feature complete, it returns whether WIFI_FEATURE_SAE_PK // is supported or not directly. return false; } /** * @return true if this device supports Wi-Fi Passpoint Terms and Conditions feature. */ public boolean isPasspointTermsAndConditionsSupported() { return isFeatureSupported(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS); } /** * @return true if this device supports WPA3 SAE Hash-to-Element. */ public boolean isWpa3SaeH2eSupported() { return isFeatureSupported(WIFI_FEATURE_SAE_H2E); } /** * @return true if this device supports Wi-Fi Display R2. */ public boolean isWifiDisplayR2Supported() { return isFeatureSupported(WIFI_FEATURE_WFD_R2); } /** * @return true if this device supports RFC 7542 decorated identity. */ public boolean isDecoratedIdentitySupported() { return isFeatureSupported(WIFI_FEATURE_DECORATED_IDENTITY); } /** * @return true if this device supports Trust On First Use (TOFU). */ public boolean isTrustOnFirstUseSupported() { return isFeatureSupported(WIFI_FEATURE_TRUST_ON_FIRST_USE); } /** * Wi-Fi Easy Connect DPP AKM enables provisioning and configuration of Wi-Fi devices without * the need of using the device PSK passphrase. * For more details, visit https://www.wi-fi.org/ and * search for "Easy Connect" or "Device Provisioning Protocol specification". * * @return true if this device supports Wi-Fi Easy-connect DPP (Device Provisioning Protocol) * AKM, false otherwise. */ public boolean isEasyConnectDppAkmSupported() { return isFeatureSupported(WIFI_FEATURE_DPP_AKM); } /** * Indicate that whether or not settings required TLS minimum version is supported. * * If the device doesn't support this capability, the minimum accepted TLS version is 1.0. * * @return true if this device supports setting TLS minimum version. */ public boolean isTlsMinimumVersionSupported() { return isFeatureSupported(WIFI_FEATURE_SET_TLS_MINIMUM_VERSION); } /** * Indicate that whether or not TLS v1.3 is supported. * * If requested minimum is not supported, it will default to the maximum supported version. * * @return true if this device supports TLS v1.3. */ public boolean isTlsV13Supported() { return isFeatureSupported(WIFI_FEATURE_TLS_V1_3); } /** * @return true if this device supports Dual Band Simultaneous (DBS) operation. */ public boolean isDualBandSimultaneousSupported() { return isFeatureSupported(WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS); } /** * @return true if this device supports TID-To-Link Mapping Negotiation. */ public boolean isTidToLinkMappingNegotiationSupported() { return isFeatureSupported(WIFI_FEATURE_T2LM_NEGOTIATION); } /** * @return true if this device supports connections to Wi-Fi WEP networks. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public boolean isWepSupported() { return isFeatureSupported(WIFI_FEATURE_WEP); } /** * @return true if this device supports connections to Wi-Fi WPA-Personal networks. * * Note that this is the older and less secure WPA-Personal protocol, not WPA2-Personal * or later protocols. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public boolean isWpaPersonalSupported() { return isFeatureSupported(WIFI_FEATURE_WPA_PERSONAL); } /** * Gets the factory Wi-Fi MAC addresses. * @return Array of String representing Wi-Fi MAC addresses sorted lexically or an empty Array * if failed. * @hide */ @NonNull @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses() { try { return mService.getFactoryMacAddresses(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"DEVICE_MOBILITY_STATE_"}, value = { DEVICE_MOBILITY_STATE_UNKNOWN, DEVICE_MOBILITY_STATE_HIGH_MVMT, DEVICE_MOBILITY_STATE_LOW_MVMT, DEVICE_MOBILITY_STATE_STATIONARY}) public @interface DeviceMobilityState {} /** * Unknown device mobility state * * @see #setDeviceMobilityState(int) * * @hide */ @SystemApi public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; /** * High movement device mobility state. * e.g. on a bike, in a motor vehicle * * @see #setDeviceMobilityState(int) * * @hide */ @SystemApi public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; /** * Low movement device mobility state. * e.g. walking, running * * @see #setDeviceMobilityState(int) * * @hide */ @SystemApi public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; /** * Stationary device mobility state * * @see #setDeviceMobilityState(int) * * @hide */ @SystemApi public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; /** * Updates the device mobility state. Wifi uses this information to adjust the interval between * Wifi scans in order to balance power consumption with scan accuracy. * The default mobility state when the device boots is {@link #DEVICE_MOBILITY_STATE_UNKNOWN}. * This API should be called whenever there is a change in the mobility state. * @param state the updated device mobility state * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(@DeviceMobilityState int state) { try { mService.setDeviceMobilityState(state); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /* Easy Connect - AKA Device Provisioning Protocol (DPP) */ /** * Easy Connect Network role: Station. * * @hide */ @SystemApi public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; /** * Easy Connect Network role: Access Point. * * @hide */ @SystemApi public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; /** @hide */ @IntDef(prefix = {"EASY_CONNECT_NETWORK_ROLE_"}, value = { EASY_CONNECT_NETWORK_ROLE_STA, EASY_CONNECT_NETWORK_ROLE_AP, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectNetworkRole { } /** Easy Connect Device information maximum allowed length */ private static final int EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH = 40; /** * Easy Connect Cryptography Curve name: prime256v1 * * @hide */ @SystemApi public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1 = 0; /** * Easy Connect Cryptography Curve name: secp384r1 * * @hide */ @SystemApi public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1 = 1; /** * Easy Connect Cryptography Curve name: secp521r1 * * @hide */ @SystemApi public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1 = 2; /** * Easy Connect Cryptography Curve name: brainpoolP256r1 * * @hide */ @SystemApi public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1 = 3; /** * Easy Connect Cryptography Curve name: brainpoolP384r1 * * @hide */ @SystemApi public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1 = 4; /** * Easy Connect Cryptography Curve name: brainpoolP512r1 * * @hide */ @SystemApi public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1 = 5; /** @hide */ @IntDef(prefix = {"EASY_CONNECT_CRYPTOGRAPHY_CURVE_"}, value = { EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1, EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1, EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1, EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1, EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1, EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectCryptographyCurve { } /** * Verbose logging mode: DISABLED. * @hide */ @SystemApi public static final int VERBOSE_LOGGING_LEVEL_DISABLED = 0; /** * Verbose logging mode: ENABLED. * @hide */ @SystemApi public static final int VERBOSE_LOGGING_LEVEL_ENABLED = 1; /** * Verbose logging mode: ENABLED_SHOW_KEY. This mode causes the Wi-Fi password and encryption * keys to be output to the logcat. This is security sensitive information useful for debugging. * This configuration is enabled for 30 seconds and then falls back to the regular verbose mode * (i.e. to {@link VERBOSE_LOGGING_LEVEL_ENABLED}). Show key mode is not persistent, i.e. * rebooting the device would fallback to the regular verbose mode. * * @hide */ @SystemApi public static final int VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY = 2; /** * Verbose logging mode: only enable for Wi-Fi Aware feature. * * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public static final int VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY = 3; /** @hide */ @IntDef(prefix = {"VERBOSE_LOGGING_LEVEL_"}, value = { VERBOSE_LOGGING_LEVEL_DISABLED, VERBOSE_LOGGING_LEVEL_ENABLED, VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY, VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY, }) @Retention(RetentionPolicy.SOURCE) public @interface VerboseLoggingLevel { } /** * Start Easy Connect (DPP) in Configurator-Initiator role. The current device will initiate * Easy Connect bootstrapping with a peer, and configure the peer with the SSID and password of * the specified network using the Easy Connect protocol on an encrypted link. * * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning) * @param selectedNetworkId Selected network ID to be sent to the peer * @param enrolleeNetworkRole The network role of the enrollee * @param callback Callback for status updates * @param executor The Executor on which to run the callback. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String enrolleeUri, int selectedNetworkId, @EasyConnectNetworkRole int enrolleeNetworkRole, @NonNull @CallbackExecutor Executor executor, @NonNull EasyConnectStatusCallback callback) { Binder binder = new Binder(); try { mService.startDppAsConfiguratorInitiator(binder, mContext.getOpPackageName(), enrolleeUri, selectedNetworkId, enrolleeNetworkRole, new EasyConnectCallbackProxy(executor, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Start Easy Connect (DPP) in Enrollee-Initiator role. The current device will initiate Easy * Connect bootstrapping with a peer, and receive the SSID and password from the peer * configurator. * * @param configuratorUri URI of the Configurator obtained separately (e.g. QR code scanning) * @param callback Callback for status updates * @param executor The Executor on which to run the callback. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String configuratorUri, @NonNull @CallbackExecutor Executor executor, @NonNull EasyConnectStatusCallback callback) { Binder binder = new Binder(); try { mService.startDppAsEnrolleeInitiator(binder, configuratorUri, new EasyConnectCallbackProxy(executor, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Start Easy Connect (DPP) in Enrollee-Responder role. * The device will: * 1. Generate a DPP bootstrap URI and return it using the * {@link EasyConnectStatusCallback#onBootstrapUriGenerated(Uri)} method. * 2. Start DPP as a Responder, waiting for an Initiator device to start the DPP * authentication process. * The caller should use the URI provided in step #1, for instance display it as a QR code * or communicate it in some other way to the initiator device. * * @param deviceInfo Device specific information to add to the DPP URI. This field allows * the users of the configurators to identify the device. * Optional - if not provided or in case of an empty string, * Info field (I:) will be skipped in the generated DPP URI. * Allowed Range of ASCII characters in deviceInfo - %x20-7E. * semicolon and space are not allowed. Due to the limitation of maximum * allowed characters in QR code, framework adds a limit to maximum * characters in deviceInfo. Users must call * {@link WifiManager#getEasyConnectMaxAllowedResponderDeviceInfoLength() * } method to know max allowed length. Violation of these rules will * result in an exception. * @param curve Elliptic curve cryptography used to generate DPP * public/private key pair. If application is not interested in a * specific curve, use specification mandated NIST P-256 elliptic curve, * {@link WifiManager#EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1}. * @param callback Callback for status updates * @param executor The Executor on which to run the callback. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) @RequiresApi(Build.VERSION_CODES.S) public void startEasyConnectAsEnrolleeResponder(@Nullable String deviceInfo, @EasyConnectCryptographyCurve int curve, @NonNull @CallbackExecutor Executor executor, @NonNull EasyConnectStatusCallback callback) { Binder binder = new Binder(); try { mService.startDppAsEnrolleeResponder(binder, deviceInfo, curve, new EasyConnectCallbackProxy(executor, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Maximum allowed length of Device specific information that can be added to the URI of * Easy Connect responder device. * @see #startEasyConnectAsEnrolleeResponder(String, int, Executor, EasyConnectStatusCallback)} * * @hide */ @SystemApi public static int getEasyConnectMaxAllowedResponderDeviceInfoLength() { return EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH; } /** * Stop or abort a current Easy Connect (DPP) session. This call, once processed, will * terminate any ongoing transaction, and clean up all associated resources. Caller should not * expect any callbacks once this call is made. However, due to the asynchronous nature of * this call, a callback may be fired if it was already pending in the queue. * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession() { try { /* Request lower layers to stop/abort and clear resources */ mService.stopDppSession(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Helper class to support Easy Connect (DPP) callbacks * * @hide */ private static class EasyConnectCallbackProxy extends IDppCallback.Stub { private final Executor mExecutor; private final EasyConnectStatusCallback mEasyConnectStatusCallback; EasyConnectCallbackProxy(Executor executor, EasyConnectStatusCallback easyConnectStatusCallback) { mExecutor = executor; mEasyConnectStatusCallback = easyConnectStatusCallback; } @Override public void onSuccessConfigReceived(int newNetworkId) { Log.d(TAG, "Easy Connect onSuccessConfigReceived callback"); Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onEnrolleeSuccess(newNetworkId); }); } @Override public void onSuccess(int status) { Log.d(TAG, "Easy Connect onSuccess callback"); Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onConfiguratorSuccess(status); }); } @Override public void onFailure(int status, String ssid, String channelList, int[] operatingClassArray) { Log.d(TAG, "Easy Connect onFailure callback"); Binder.clearCallingIdentity(); mExecutor.execute(() -> { SparseArray channelListArray = parseDppChannelList(channelList); mEasyConnectStatusCallback.onFailure(status, ssid, channelListArray, operatingClassArray); }); } @Override public void onProgress(int status) { Log.d(TAG, "Easy Connect onProgress callback"); Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onProgress(status); }); } @Override public void onBootstrapUriGenerated(@NonNull String uri) { Log.d(TAG, "Easy Connect onBootstrapUriGenerated callback"); if (!SdkLevel.isAtLeastS()) { Log.e(TAG, "Easy Connect bootstrap URI callback supported only on S+"); return; } Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onBootstrapUriGenerated(Uri.parse(uri)); }); } } /** * Interface for Wi-Fi usability statistics listener. Should be implemented by applications and * set when calling {@link WifiManager#addOnWifiUsabilityStatsListener(Executor, * OnWifiUsabilityStatsListener)}. * * @hide */ @SystemApi public interface OnWifiUsabilityStatsListener { /** * Called when Wi-Fi usability statistics is updated. * * @param seqNum The sequence number of statistics, used to derive the timing of updated * Wi-Fi usability statistics, set by framework and incremented by one after * each update. * @param isSameBssidAndFreq The flag to indicate whether the BSSID and the frequency of * network stays the same or not relative to the last update of * Wi-Fi usability stats. * @param stats The updated Wi-Fi usability statistics. */ void onWifiUsabilityStats(int seqNum, boolean isSameBssidAndFreq, @NonNull WifiUsabilityStatsEntry stats); } /** * Interface for Wi-Fi verbose logging status listener. Should be implemented by applications * and set when calling {@link WifiManager#addWifiVerboseLoggingStatusListener(Executor, * WifiVerboseLoggingStatusListener)}. * * @hide */ @SystemApi public interface WifiVerboseLoggingStatusChangedListener { /** * Called when Wi-Fi verbose logging setting is updated. * * @param enabled true if verbose logging is enabled, false if verbose logging is disabled. */ void onWifiVerboseLoggingStatusChanged(boolean enabled); } /** * Adds a listener for Wi-Fi usability statistics. See {@link OnWifiUsabilityStatsListener}. * Multiple listeners can be added. Callers will be invoked periodically by framework to * inform clients about the current Wi-Fi usability statistics. Callers can remove a previously * added listener using * {@link #removeOnWifiUsabilityStatsListener(OnWifiUsabilityStatsListener)}. * * @param executor The executor on which callback will be invoked. * @param listener Listener for Wifi usability statistics. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull @CallbackExecutor Executor executor, @NonNull OnWifiUsabilityStatsListener listener) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (listener == null) throw new IllegalArgumentException("listener cannot be null"); if (mVerboseLoggingEnabled) { Log.v(TAG, "addOnWifiUsabilityStatsListener: listener=" + listener); } try { synchronized (sOnWifiUsabilityStatsListenerMap) { IOnWifiUsabilityStatsListener.Stub binderCallback = new IOnWifiUsabilityStatsListener.Stub() { @Override public void onWifiUsabilityStats(int seqNum, boolean isSameBssidAndFreq, WifiUsabilityStatsEntry stats) { if (mVerboseLoggingEnabled) { Log.v(TAG, "OnWifiUsabilityStatsListener: " + "onWifiUsabilityStats: seqNum=" + seqNum); } Binder.clearCallingIdentity(); executor.execute(() -> listener.onWifiUsabilityStats( seqNum, isSameBssidAndFreq, stats)); } }; sOnWifiUsabilityStatsListenerMap.put(System.identityHashCode(listener), binderCallback); mService.addOnWifiUsabilityStatsListener(binderCallback); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to remove a previously registered listener. After calling this method, * applications will no longer receive Wi-Fi usability statistics. * * @param listener Listener to remove the Wi-Fi usability statistics. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull OnWifiUsabilityStatsListener listener) { if (listener == null) throw new IllegalArgumentException("listener cannot be null"); if (mVerboseLoggingEnabled) { Log.v(TAG, "removeOnWifiUsabilityStatsListener: listener=" + listener); } try { synchronized (sOnWifiUsabilityStatsListenerMap) { int listenerIdentifier = System.identityHashCode(listener); if (!sOnWifiUsabilityStatsListenerMap.contains(listenerIdentifier)) { Log.w(TAG, "Unknown external callback " + listenerIdentifier); return; } mService.removeOnWifiUsabilityStatsListener( sOnWifiUsabilityStatsListenerMap.get(listenerIdentifier)); sOnWifiUsabilityStatsListenerMap.remove(listenerIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Provide a Wi-Fi usability score information to be recorded (but not acted upon) by the * framework. The Wi-Fi usability score is derived from {@link OnWifiUsabilityStatsListener} * where a score is matched to Wi-Fi usability statistics using the sequence number. The score * is used to quantify whether Wi-Fi is usable in a future time. * * @param seqNum Sequence number of the Wi-Fi usability score. * @param score The Wi-Fi usability score, expected range: [0, 100]. * @param predictionHorizonSec Prediction horizon of the Wi-Fi usability score in second, * expected range: [0, 30]. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) { try { mService.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Abstract class for scan results callback. Should be extended by applications and set when * calling {@link WifiManager#registerScanResultsCallback(Executor, ScanResultsCallback)}. */ public abstract static class ScanResultsCallback { private final ScanResultsCallbackProxy mScanResultsCallbackProxy; public ScanResultsCallback() { mScanResultsCallbackProxy = new ScanResultsCallbackProxy(); } /** * Called when new scan results are available. * Clients should use {@link WifiManager#getScanResults()} to get the scan results. */ public abstract void onScanResultsAvailable(); /*package*/ @NonNull ScanResultsCallbackProxy getProxy() { return mScanResultsCallbackProxy; } private static class ScanResultsCallbackProxy extends IScanResultsCallback.Stub { private final Object mLock = new Object(); @Nullable @GuardedBy("mLock") private Executor mExecutor; @Nullable @GuardedBy("mLock") private ScanResultsCallback mCallback; ScanResultsCallbackProxy() { mCallback = null; mExecutor = null; } /*package*/ void initProxy(@NonNull Executor executor, @NonNull ScanResultsCallback callback) { synchronized (mLock) { mExecutor = executor; mCallback = callback; } } /*package*/ void cleanUpProxy() { synchronized (mLock) { mExecutor = null; mCallback = null; } } @Override public void onScanResultsAvailable() { ScanResultsCallback callback; Executor executor; synchronized (mLock) { executor = mExecutor; callback = mCallback; } if (callback == null || executor == null) { return; } Binder.clearCallingIdentity(); executor.execute(callback::onScanResultsAvailable); } } } /** * Register a callback for Scan Results. See {@link ScanResultsCallback}. * Caller will receive the event when scan results are available. * Caller should use {@link WifiManager#getScanResults()} requires * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} to get the scan results. * Caller can remove a previously registered callback using * {@link WifiManager#unregisterScanResultsCallback(ScanResultsCallback)} * Same caller can add multiple listeners. *

    * Applications should have the * {@link android.Manifest.permission#ACCESS_WIFI_STATE} permission. Callers * without the permission will trigger a {@link java.lang.SecurityException}. *

    * * @param executor The executor to execute the callback of the {@code callback} object. * @param callback callback for Scan Results events */ @RequiresPermission(ACCESS_WIFI_STATE) public void registerScanResultsCallback(@NonNull @CallbackExecutor Executor executor, @NonNull ScanResultsCallback callback) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerScanResultsCallback: callback=" + callback + ", executor=" + executor); ScanResultsCallback.ScanResultsCallbackProxy proxy = callback.getProxy(); proxy.initProxy(executor, callback); try { mService.registerScanResultsCallback(proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to unregister a previously registered callback. After calling this method, * applications will no longer receive Scan Results events. * * @param callback callback to unregister for Scan Results events */ @RequiresPermission(ACCESS_WIFI_STATE) public void unregisterScanResultsCallback(@NonNull ScanResultsCallback callback) { if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "unregisterScanResultsCallback: Callback=" + callback); ScanResultsCallback.ScanResultsCallbackProxy proxy = callback.getProxy(); try { mService.unregisterScanResultsCallback(proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { proxy.cleanUpProxy(); } } /** * Interface for suggestion connection status listener. * Should be implemented by applications and set when calling * {@link WifiManager#addSuggestionConnectionStatusListener( * Executor, SuggestionConnectionStatusListener)}. */ public interface SuggestionConnectionStatusListener { /** * Called when the framework attempted to connect to a suggestion provided by the * registering app, but the connection to the suggestion failed. * @param wifiNetworkSuggestion The suggestion which failed to connect. * @param failureReason the connection failure reason code. */ void onConnectionStatus( @NonNull WifiNetworkSuggestion wifiNetworkSuggestion, @SuggestionConnectionStatusCode int failureReason); } private class SuggestionConnectionStatusListenerProxy extends ISuggestionConnectionStatusListener.Stub { private final Executor mExecutor; private final SuggestionConnectionStatusListener mListener; SuggestionConnectionStatusListenerProxy(@NonNull Executor executor, @NonNull SuggestionConnectionStatusListener listener) { mExecutor = executor; mListener = listener; } @Override public void onConnectionStatus(@NonNull WifiNetworkSuggestion wifiNetworkSuggestion, int failureReason) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onConnectionStatus(wifiNetworkSuggestion, failureReason)); } } /** * Interface for local-only connection failure listener. * Should be implemented by applications and set when calling * {@link WifiManager#addLocalOnlyConnectionFailureListener(Executor, LocalOnlyConnectionFailureListener)} */ public interface LocalOnlyConnectionFailureListener { /** * Called when the framework attempted to connect to a local-only network requested by the * registering app, but the connection to the network failed. * @param wifiNetworkSpecifier The {@link WifiNetworkSpecifier} which failed to connect. * @param failureReason the connection failure reason code. */ void onConnectionFailed( @NonNull WifiNetworkSpecifier wifiNetworkSpecifier, @LocalOnlyConnectionStatusCode int failureReason); } private static class LocalOnlyConnectionStatusListenerProxy extends ILocalOnlyConnectionStatusListener.Stub { private final Executor mExecutor; private final LocalOnlyConnectionFailureListener mListener; LocalOnlyConnectionStatusListenerProxy(@NonNull Executor executor, @NonNull LocalOnlyConnectionFailureListener listener) { mExecutor = executor; mListener = listener; } @Override public void onConnectionStatus(@NonNull WifiNetworkSpecifier networkSpecifier, int failureReason) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onConnectionFailed(networkSpecifier, failureReason)); } } /** * Add a listener listening to wifi verbose logging changes. * See {@link WifiVerboseLoggingStatusChangedListener}. * Caller can remove a previously registered listener using * {@link WifiManager#removeWifiVerboseLoggingStatusChangedListener( * WifiVerboseLoggingStatusChangedListener)} * Same caller can add multiple listeners to monitor the event. *

    * Applications should have the * {@link android.Manifest.permission#ACCESS_WIFI_STATE}. * Callers without the permission will trigger a {@link java.lang.SecurityException}. *

    * @param executor The executor to execute the listener of the {@code listener} object. * @param listener listener for changes in wifi verbose logging. * * @hide */ @SystemApi @RequiresPermission(ACCESS_WIFI_STATE) public void addWifiVerboseLoggingStatusChangedListener( @NonNull @CallbackExecutor Executor executor, @NonNull WifiVerboseLoggingStatusChangedListener listener) { if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); if (executor == null) throw new IllegalArgumentException("Executor cannot be null"); if (mVerboseLoggingEnabled) { Log.v(TAG, "addWifiVerboseLoggingStatusChangedListener listener=" + listener + ", executor=" + executor); } try { synchronized (sWifiVerboseLoggingStatusChangedListenerMap) { IWifiVerboseLoggingStatusChangedListener.Stub binderCallback = new IWifiVerboseLoggingStatusChangedListener.Stub() { @Override public void onStatusChanged(boolean enabled) { if (mVerboseLoggingEnabled) { Log.v(TAG, "WifiVerboseLoggingStatusListener: " + "onVerboseLoggingStatusChanged: enabled=" + enabled); } Binder.clearCallingIdentity(); executor.execute(() -> listener.onWifiVerboseLoggingStatusChanged( enabled)); } }; sWifiVerboseLoggingStatusChangedListenerMap.put(System.identityHashCode(listener), binderCallback); mService.addWifiVerboseLoggingStatusChangedListener(binderCallback); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to remove a previously registered listener. *

    * Applications should have the * {@link android.Manifest.permission#ACCESS_WIFI_STATE}. * Callers without the permission will trigger a {@link java.lang.SecurityException}. *

    * @param listener listener to remove. * * @hide */ @SystemApi @RequiresPermission(ACCESS_WIFI_STATE) public void removeWifiVerboseLoggingStatusChangedListener( @NonNull WifiVerboseLoggingStatusChangedListener listener) { if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); Log.v(TAG, "removeWifiVerboseLoggingStatusChangedListener: listener=" + listener); try { synchronized (sWifiVerboseLoggingStatusChangedListenerMap) { int listenerIdentifier = System.identityHashCode(listener); if (!sWifiVerboseLoggingStatusChangedListenerMap.contains(listenerIdentifier)) { Log.w(TAG, "Unknown external callback " + listenerIdentifier); return; } mService.removeWifiVerboseLoggingStatusChangedListener( sWifiVerboseLoggingStatusChangedListenerMap.get(listenerIdentifier)); sWifiVerboseLoggingStatusChangedListenerMap.remove(listenerIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Add a listener for suggestion networks. See {@link SuggestionConnectionStatusListener}. * Caller will receive the event when suggested network have connection failure. * Caller can remove a previously registered listener using * {@link WifiManager#removeSuggestionConnectionStatusListener( * SuggestionConnectionStatusListener)} * Same caller can add multiple listeners to monitor the event. *

    * Applications should have the * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and * {@link android.Manifest.permission#ACCESS_WIFI_STATE} permissions. * Callers without the permission will trigger a {@link java.lang.SecurityException}. *

    * * @param executor The executor to execute the listener of the {@code listener} object. * @param listener listener for suggestion network connection failure. */ @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) public void addSuggestionConnectionStatusListener(@NonNull @CallbackExecutor Executor executor, @NonNull SuggestionConnectionStatusListener listener) { if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); if (executor == null) throw new IllegalArgumentException("Executor cannot be null"); Log.v(TAG, "addSuggestionConnectionStatusListener listener=" + listener + ", executor=" + executor); try { synchronized (sSuggestionConnectionStatusListenerMap) { ISuggestionConnectionStatusListener.Stub binderCallback = new SuggestionConnectionStatusListenerProxy(executor, listener); sSuggestionConnectionStatusListenerMap.put(System.identityHashCode(listener), binderCallback); mService.registerSuggestionConnectionStatusListener(binderCallback, mContext.getOpPackageName(), mContext.getAttributionTag()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to remove a previously registered listener. After calling this method, * applications will no longer receive suggestion connection events through that listener. * * @param listener listener to remove. */ @RequiresPermission(ACCESS_WIFI_STATE) public void removeSuggestionConnectionStatusListener( @NonNull SuggestionConnectionStatusListener listener) { if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); Log.v(TAG, "removeSuggestionConnectionStatusListener: listener=" + listener); try { synchronized (sSuggestionConnectionStatusListenerMap) { int listenerIdentifier = System.identityHashCode(listener); if (!sSuggestionConnectionStatusListenerMap.contains(listenerIdentifier)) { Log.w(TAG, "Unknown external callback " + listenerIdentifier); return; } mService.unregisterSuggestionConnectionStatusListener( sSuggestionConnectionStatusListenerMap.get(listenerIdentifier), mContext.getOpPackageName()); sSuggestionConnectionStatusListenerMap.remove(listenerIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Add a listener for local-only networks. See {@link WifiNetworkSpecifier}. * Specify the caller will only get connection failures for networks they requested. * Caller can remove a previously registered listener using * {@link WifiManager#removeLocalOnlyConnectionFailureListener(LocalOnlyConnectionFailureListener)} * Same caller can add multiple listeners to monitor the event. *

    * Applications should have the {@link android.Manifest.permission#ACCESS_WIFI_STATE} * permissions. * Callers without the permission will trigger a {@link java.lang.SecurityException}. *

    * * @param executor The executor to execute the listener of the {@code listener} object. * @param listener listener for local-only network connection failure. */ @RequiresPermission(ACCESS_WIFI_STATE) public void addLocalOnlyConnectionFailureListener(@NonNull @CallbackExecutor Executor executor, @NonNull LocalOnlyConnectionFailureListener listener) { if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); if (executor == null) throw new IllegalArgumentException("Executor cannot be null"); try { synchronized (sLocalOnlyConnectionStatusListenerMap) { if (sLocalOnlyConnectionStatusListenerMap .contains(System.identityHashCode(listener))) { Log.w(TAG, "Same listener already registered"); return; } ILocalOnlyConnectionStatusListener.Stub binderCallback = new LocalOnlyConnectionStatusListenerProxy(executor, listener); sLocalOnlyConnectionStatusListenerMap.put(System.identityHashCode(listener), binderCallback); mService.addLocalOnlyConnectionStatusListener(binderCallback, mContext.getOpPackageName(), mContext.getAttributionTag()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to remove a previously registered listener. After calling this method, * applications will no longer receive local-only connection events through that listener. * * @param listener listener to remove. */ @RequiresPermission(ACCESS_WIFI_STATE) public void removeLocalOnlyConnectionFailureListener( @NonNull LocalOnlyConnectionFailureListener listener) { if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); try { synchronized (sLocalOnlyConnectionStatusListenerMap) { int listenerIdentifier = System.identityHashCode(listener); if (!sLocalOnlyConnectionStatusListenerMap.contains(listenerIdentifier)) { Log.w(TAG, "Unknown external callback " + listenerIdentifier); return; } mService.removeLocalOnlyConnectionStatusListener( sLocalOnlyConnectionStatusListenerMap.get(listenerIdentifier), mContext.getOpPackageName()); sLocalOnlyConnectionStatusListenerMap.remove(listenerIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Parse the list of channels the DPP enrollee reports when it fails to find an AP. * * @param channelList List of channels in the format defined in the DPP specification. * @return A parsed sparse array, where the operating class is the key. * @hide */ @VisibleForTesting public static SparseArray parseDppChannelList(String channelList) { SparseArray channelListArray = new SparseArray<>(); if (TextUtils.isEmpty(channelList)) { return channelListArray; } StringTokenizer str = new StringTokenizer(channelList, ","); String classStr = null; List channelsInClass = new ArrayList<>(); try { while (str.hasMoreElements()) { String cur = str.nextToken(); /** * Example for a channel list: * * 81/1,2,3,4,5,6,7,8,9,10,11,115/36,40,44,48,118/52,56,60,64,121/100,104,108,112, * 116,120,124,128,132,136,140,0/144,124/149,153,157,161,125/165 * * Detect operating class by the delimiter of '/' and use a string tokenizer with * ',' as a delimiter. */ int classDelim = cur.indexOf('/'); if (classDelim != -1) { if (classStr != null) { // Store the last channel array in the sparse array, where the operating // class is the key (as an integer). int[] channelsArray = new int[channelsInClass.size()]; for (int i = 0; i < channelsInClass.size(); i++) { channelsArray[i] = channelsInClass.get(i); } channelListArray.append(Integer.parseInt(classStr), channelsArray); channelsInClass = new ArrayList<>(); } // Init a new operating class and store the first channel classStr = cur.substring(0, classDelim); String channelStr = cur.substring(classDelim + 1); channelsInClass.add(Integer.parseInt(channelStr)); } else { if (classStr == null) { // Invalid format Log.e(TAG, "Cannot parse DPP channel list"); return new SparseArray<>(); } channelsInClass.add(Integer.parseInt(cur)); } } // Store the last array if (classStr != null) { int[] channelsArray = new int[channelsInClass.size()]; for (int i = 0; i < channelsInClass.size(); i++) { channelsArray[i] = channelsInClass.get(i); } channelListArray.append(Integer.parseInt(classStr), channelsArray); } return channelListArray; } catch (NumberFormatException e) { Log.e(TAG, "Cannot parse DPP channel list"); return new SparseArray<>(); } } /** * Callback interface for framework to receive network status updates and trigger of updating * {@link WifiUsabilityStatsEntry}. * * @hide */ @SystemApi public interface ScoreUpdateObserver { /** * Called by applications to indicate network status. For applications targeting * {@link android.os.Build.VERSION_CODES#S} or above: The score is not used to take action * on network selection but for the purpose of Wifi metric collection only; Network * selection is influenced by inputs from * {@link ScoreUpdateObserver#notifyStatusUpdate(int, boolean)}, * {@link ScoreUpdateObserver#requestNudOperation(int, boolean)}, and * {@link ScoreUpdateObserver#blocklistCurrentBssid(int)}. * * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#onStart(int)}. * @param score The score representing link quality of current Wi-Fi network connection. * Populated by connected network scorer in applications.. */ void notifyScoreUpdate(int sessionId, int score); /** * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}. * To receive update applications need to add WifiUsabilityStatsEntry listener. See * {@link addOnWifiUsabilityStatsListener(Executor, OnWifiUsabilityStatsListener)}. * * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#onStart(int)}. */ void triggerUpdateOfWifiUsabilityStats(int sessionId); /** * Called by applications to indicate whether current Wi-Fi network is usable or not. * * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#onStart(int)}. * @param isUsable The boolean representing whether current Wi-Fi network is usable, and it * may be sent to ConnectivityService and used for setting default network. * Populated by connected network scorer in applications. */ @RequiresApi(Build.VERSION_CODES.S) default void notifyStatusUpdate(int sessionId, boolean isUsable) {} /** * Called by applications to start a NUD (Neighbor Unreachability Detection) operation. The * framework throttles NUD operations to no more frequently than every five seconds * (see {@link WifiScoreReport#NUD_THROTTLE_MILLIS}). The framework keeps track of requests * and executes them as soon as possible based on the throttling criteria. * * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#onStart(int)}. */ @RequiresApi(Build.VERSION_CODES.S) default void requestNudOperation(int sessionId) {} /** * Called by applications to blocklist currently connected BSSID. No blocklisting operation * if called after disconnection. * * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#onStart(int)}. */ @RequiresApi(Build.VERSION_CODES.S) default void blocklistCurrentBssid(int sessionId) {} } /** * Callback proxy for {@link ScoreUpdateObserver} objects. * * @hide */ private class ScoreUpdateObserverProxy implements ScoreUpdateObserver { private final IScoreUpdateObserver mScoreUpdateObserver; private ScoreUpdateObserverProxy(IScoreUpdateObserver observer) { mScoreUpdateObserver = observer; } @Override public void notifyScoreUpdate(int sessionId, int score) { try { mScoreUpdateObserver.notifyScoreUpdate(sessionId, score); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override public void triggerUpdateOfWifiUsabilityStats(int sessionId) { try { mScoreUpdateObserver.triggerUpdateOfWifiUsabilityStats(sessionId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override public void notifyStatusUpdate(int sessionId, boolean isUsable) { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } try { mScoreUpdateObserver.notifyStatusUpdate(sessionId, isUsable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override public void requestNudOperation(int sessionId) { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } try { mScoreUpdateObserver.requestNudOperation(sessionId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override public void blocklistCurrentBssid(int sessionId) { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } try { mScoreUpdateObserver.blocklistCurrentBssid(sessionId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Interface for Wi-Fi connected network scorer. Should be implemented by applications and set * when calling * {@link WifiManager#setWifiConnectedNetworkScorer(Executor, WifiConnectedNetworkScorer)}. * * @hide */ @SystemApi public interface WifiConnectedNetworkScorer { /** * Called by framework to indicate the start of a network connection. * @param sessionId The ID to indicate current Wi-Fi network connection. * @deprecated This API is deprecated. Please use * {@link WifiConnectedNetworkScorer#onStart(WifiConnectedSessionInfo)}. */ @Deprecated default void onStart(int sessionId) {} /** * Called by framework to indicate the start of a network connection. * @param sessionInfo The session information to indicate current Wi-Fi network connection. * See {@link WifiConnectedSessionInfo}. */ default void onStart(@NonNull WifiConnectedSessionInfo sessionInfo) { onStart(sessionInfo.getSessionId()); } /** * Called by framework to indicate the end of a network connection. * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#onStart(int)}. */ void onStop(int sessionId); /** * Framework sets callback for score change events after application sets its scorer. * @param observerImpl The instance for {@link WifiManager#ScoreUpdateObserver}. Should be * implemented and instantiated by framework. */ void onSetScoreUpdateObserver(@NonNull ScoreUpdateObserver observerImpl); /** * Called by framework to indicate the user accepted a dialog to switch to a new network. * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#onStart(int)}. * @param targetNetworkId Network ID of the target network. * @param targetBssid BSSID of the target network. */ default void onNetworkSwitchAccepted( int sessionId, int targetNetworkId, @NonNull String targetBssid) { // No-op. } /** * Called by framework to indicate the user rejected a dialog to switch to new network. * @param sessionId The ID to indicate current Wi-Fi network connection obtained from * {@link WifiConnectedNetworkScorer#onStart(int)}. * @param targetNetworkId Network ID of the target network. * @param targetBssid BSSID of the target network. */ default void onNetworkSwitchRejected( int sessionId, int targetNetworkId, @NonNull String targetBssid) { // No-op. } } /** * Callback registered with {@link WifiManager#setExternalPnoScanRequest(List, int[], Executor, * PnoScanResultsCallback)}. Returns status and result information on offloaded external PNO * requests. * @hide */ @SystemApi public interface PnoScanResultsCallback { /** * A status code returned by {@link #onRegisterFailed(int)}. * Unknown failure. */ int REGISTER_PNO_CALLBACK_UNKNOWN = 0; /** * A status code returned by {@link #onRegisterFailed(int)}. * A callback has already been registered by the caller. */ int REGISTER_PNO_CALLBACK_ALREADY_REGISTERED = 1; /** * A status code returned by {@link #onRegisterFailed(int)}. * The platform is unable to serve this request because another app has a PNO scan request * active. */ int REGISTER_PNO_CALLBACK_RESOURCE_BUSY = 2; /** * A status code returned by {@link #onRegisterFailed(int)}. * PNO scans are not supported on this device. */ int REGISTER_PNO_CALLBACK_PNO_NOT_SUPPORTED = 3; /** @hide */ @IntDef(prefix = { "REGISTER_PNO_CALLBACK_" }, value = { REGISTER_PNO_CALLBACK_UNKNOWN, REGISTER_PNO_CALLBACK_ALREADY_REGISTERED, REGISTER_PNO_CALLBACK_RESOURCE_BUSY, REGISTER_PNO_CALLBACK_PNO_NOT_SUPPORTED }) @Retention(RetentionPolicy.SOURCE) public @interface RegisterFailureReason {} /** * A status code returned by {@link #onRemoved(int)}. * Unknown reason. */ int REMOVE_PNO_CALLBACK_UNKNOWN = 0; /** * A status code returned by {@link #onRemoved(int)}. * This Callback is automatically removed after results ScanResults are delivered. */ int REMOVE_PNO_CALLBACK_RESULTS_DELIVERED = 1; /** * A status code returned by {@link #onRemoved(int)}. * This callback has been unregistered via {@link WifiManager#clearExternalPnoScanRequest()} */ int REMOVE_PNO_CALLBACK_UNREGISTERED = 2; /** @hide */ @IntDef(prefix = { "REMOVE_PNO_CALLBACK_" }, value = { REMOVE_PNO_CALLBACK_UNKNOWN, REMOVE_PNO_CALLBACK_RESULTS_DELIVERED, REMOVE_PNO_CALLBACK_UNREGISTERED }) @Retention(RetentionPolicy.SOURCE) public @interface RemovalReason {} /** * Called when PNO scan finds one of the requested SSIDs. This is a one time callback. * After results are reported the callback will be automatically unregistered. */ void onScanResultsAvailable(@NonNull List scanResults); /** * Called when this callback has been successfully registered. */ void onRegisterSuccess(); /** * Called when this callback failed to register with the failure reason. * See {@link RegisterFailureReason} for details. */ void onRegisterFailed(@RegisterFailureReason int reason); /** * Called when this callback has been unregistered from the Wi-Fi subsystem. * See {@link RemovalReason} for details. */ void onRemoved(@RemovalReason int reason); } private class PnoScanResultsCallbackProxy extends IPnoScanResultsCallback.Stub { private Executor mExecutor; private PnoScanResultsCallback mCallback; PnoScanResultsCallbackProxy(@NonNull Executor executor, @NonNull PnoScanResultsCallback callback) { mExecutor = executor; mCallback = callback; } @Override public void onScanResultsAvailable(List scanResults) { if (mVerboseLoggingEnabled) { Log.v(TAG, "PnoScanResultsCallback: " + "onScanResultsAvailable"); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mCallback.onScanResultsAvailable(scanResults)); } @Override public void onRegisterSuccess() { if (mVerboseLoggingEnabled) { Log.v(TAG, "PnoScanResultsCallback: " + "onRegisterSuccess"); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mCallback.onRegisterSuccess()); } @Override public void onRegisterFailed(int reason) { if (mVerboseLoggingEnabled) { Log.v(TAG, "PnoScanResultsCallback: " + "onRegisterFailed " + reason); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mCallback.onRegisterFailed(reason)); } @Override public void onRemoved(int reason) { if (mVerboseLoggingEnabled) { Log.v(TAG, "PnoScanResultsCallback: " + "onRemoved"); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mCallback.onRemoved(reason)); } } /** * Callback proxy for {@link WifiConnectedNetworkScorer} objects. * * @hide */ private class WifiConnectedNetworkScorerProxy extends IWifiConnectedNetworkScorer.Stub { private Executor mExecutor; private WifiConnectedNetworkScorer mScorer; WifiConnectedNetworkScorerProxy(Executor executor, WifiConnectedNetworkScorer scorer) { mExecutor = executor; mScorer = scorer; } @Override public void onStart(@NonNull WifiConnectedSessionInfo sessionInfo) { if (mVerboseLoggingEnabled) { Log.v(TAG, "WifiConnectedNetworkScorer: " + "onStart: sessionInfo=" + sessionInfo); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mScorer.onStart(sessionInfo)); } @Override public void onStop(int sessionId) { if (mVerboseLoggingEnabled) { Log.v(TAG, "WifiConnectedNetworkScorer: " + "onStop: sessionId=" + sessionId); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mScorer.onStop(sessionId)); } @Override public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) { if (mVerboseLoggingEnabled) { Log.v(TAG, "WifiConnectedNetworkScorer: " + "onSetScoreUpdateObserver: observerImpl=" + observerImpl); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mScorer.onSetScoreUpdateObserver( new ScoreUpdateObserverProxy(observerImpl))); } @Override public void onNetworkSwitchAccepted( int sessionId, int targetNetworkId, @NonNull String targetBssid) { if (mVerboseLoggingEnabled) { Log.v(TAG, "WifiConnectedNetworkScorer: onNetworkSwitchAccepted:" + " sessionId=" + sessionId + " targetNetworkId=" + targetNetworkId + " targetBssid=" + targetBssid); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mScorer.onNetworkSwitchAccepted( sessionId, targetNetworkId, targetBssid)); } @Override public void onNetworkSwitchRejected( int sessionId, int targetNetworkId, @NonNull String targetBssid) { if (mVerboseLoggingEnabled) { Log.v(TAG, "WifiConnectedNetworkScorer: onNetworkSwitchRejected:" + " sessionId=" + sessionId + " targetNetworkId=" + targetNetworkId + " targetBssid=" + targetBssid); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mScorer.onNetworkSwitchRejected( sessionId, targetNetworkId, targetBssid)); } } /** * This API allows the caller to program up to 2 SSIDs for PNO scans. PNO scans are offloaded * to the Wi-Fi chip when the device is inactive (typically screen-off). * If the screen is currently off when this API is called, then a PNO scan including the * requested SSIDs will immediately get started. If the screen is on when this API is called, * the requested SSIDs will get included for PNO scans the next time the screen turns off. *

    * Note, due to PNO being a limited resource, only one external PNO request is supported, and * calling this API will fail if an external PNO scan request is already registered by another * caller. If the caller that has already registered a callback calls this API again, the new * callback will override the previous one. *

    * After this API is called, {@link PnoScanResultsCallback#onRegisterSuccess()} will be invoked * if the operation is successful, or {@link PnoScanResultsCallback#onRegisterFailed(int)} will * be invoked if the operation failed. *

    * {@link PnoScanResultsCallback#onRemoved(int)} will be invoked to notify the caller when the * external PNO scan request is removed, which will happen when one of the following events * happen: *

    *
      *
    • Upon finding any of the requested SSIDs through either a connectivity scan or PNO scan, * the matching ScanResults will be returned * via {@link PnoScanResultsCallback#onScanResultsAvailable(List)}, and the registered PNO * scan request will get automatically removed.
    • *
    • The external PNO scan request is removed by a call to * {@link #clearExternalPnoScanRequest()}
    • *
    * * @param ssids The list of SSIDs to request for PNO scan. * @param frequencies Provide as hint a list of up to 10 frequencies to be used for PNO scan. * Each frequency should be in MHz. For example 2412 and 5180 are valid * frequencies. {@link WifiInfo#getFrequency()} is a location where this * information could be obtained. If a null or empty array is provided, the * Wi-Fi framework will automatically decide the list of frequencies to scan. * @param executor The executor on which callback will be invoked. * @param callback For the calling application to receive results and status updates. * * @throws SecurityException if the caller does not have permission. * @throws IllegalArgumentException if the caller provided invalid inputs. * @throws UnsupportedOperationException if this API is not supported on this SDK version. * @hide */ @SystemApi @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}) @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void setExternalPnoScanRequest(@NonNull List ssids, @Nullable int[] frequencies, @NonNull @CallbackExecutor Executor executor, @NonNull PnoScanResultsCallback callback) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); try { mService.setExternalPnoScanRequest(new Binder(), new PnoScanResultsCallbackProxy(executor, callback), ssids, frequencies == null ? new int[0] : frequencies, mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Wi-Fi Preferred Network Offload (PNO) scanning offloads scanning to the chip to save power * when Wi-Fi is disconnected and the screen is off. See * {@link https://source.android.com/docs/core/connect/wifi-scan} for more details. *

    * This API can be used to enable or disable PNO scanning. After boot, PNO scanning is enabled * by default. When PNO scanning is disabled, the Wi-Fi framework will not trigger scans at all * when the screen is off. This can be used to save power on devices with small batteries. * * @param enabled True - enable PNO scanning * False - disable PNO scanning * @param enablePnoScanAfterWifiToggle True - Wifi being enabled by * {@link #setWifiEnabled(boolean)} will re-enable PNO * scanning. * False - Wifi being enabled by * {@link #setWifiEnabled(boolean)} will not re-enable PNO * scanning. * * @throws SecurityException if the caller does not have permission. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi @RequiresPermission( anyOf = {MANAGE_WIFI_NETWORK_SELECTION, NETWORK_SETTINGS, NETWORK_SETUP_WIZARD}) public void setPnoScanState(@PnoScanState int pnoScanState) { try { boolean enabled = false; boolean enablePnoScanAfterWifiToggle = false; switch (pnoScanState) { case PNO_SCAN_STATE_DISABLED_UNTIL_REBOOT: break; case PNO_SCAN_STATE_DISABLED_UNTIL_WIFI_TOGGLE: enablePnoScanAfterWifiToggle = true; break; case PNO_SCAN_STATE_ENABLED: enabled = true; break; default: throw new IllegalArgumentException("Invalid PnoScanState"); } mService.setPnoScanEnabled(enabled, enablePnoScanAfterWifiToggle, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Wi-Fi Preferred Network Offload (PNO) scanning offloads scanning to the chip to save power * when Wi-Fi is disconnected and the screen is off. See * {@link https://source.android.com/docs/core/connect/wifi-scan} for more details. *

    * This API can be used to enable or disable PNO scanning. After boot, PNO scanning is enabled * by default. When PNO scanning is disabled, the Wi-Fi framework will not trigger scans at all * when the screen is off. This can be used to save power on devices with small batteries. * * @param enabled True - enable PNO scanning * False - disable PNO scanning * @param enablePnoScanAfterWifiToggle True - Wifi being enabled by * {@link #setWifiEnabled(boolean)} will re-enable PNO * scanning. * False - Wifi being enabled by * {@link #setWifiEnabled(boolean)} will not re-enable PNO * scanning. * * @throws SecurityException if the caller does not have permission. * @hide */ @RequiresPermission( anyOf = {MANAGE_WIFI_NETWORK_SELECTION, NETWORK_SETTINGS, NETWORK_SETUP_WIZARD}) public void setPnoScanEnabled(boolean enabled, boolean enablePnoScanAfterWifiToggle) { try { mService.setPnoScanEnabled(enabled, enablePnoScanAfterWifiToggle, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Clear the current PNO scan request that's been set by the calling UID. Note, the call will * be no-op if the current PNO scan request is set by a different UID. * * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void clearExternalPnoScanRequest() { try { mService.clearExternalPnoScanRequest(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns information about the last caller of an API. * * @param apiType The type of API to request information for the last caller. * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return 2 arguments. * {@code String} the name of the package that performed the last API * call. {@code Boolean} the value associated with the last API call. * * @throws SecurityException if the caller does not have permission. * @throws IllegalArgumentException if the caller provided invalid inputs. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void getLastCallerInfoForApi(@ApiType int apiType, @NonNull @CallbackExecutor Executor executor, @NonNull BiConsumer resultsCallback) { if (executor == null) { throw new IllegalArgumentException("executor can't be null"); } if (resultsCallback == null) { throw new IllegalArgumentException("resultsCallback can't be null"); } try { mService.getLastCallerInfoForApi(apiType, new ILastCallerListener.Stub() { @Override public void onResult(String packageName, boolean enabled) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(packageName, enabled); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Set a callback for Wi-Fi connected network scorer. See {@link WifiConnectedNetworkScorer}. * Only a single scorer can be set. Caller will be invoked periodically by framework to inform * client about start and stop of Wi-Fi connection. Caller can clear a previously set scorer * using {@link clearWifiConnectedNetworkScorer()}. * * @param executor The executor on which callback will be invoked. * @param scorer Scorer for Wi-Fi network implemented by application. * @return true Scorer is set successfully. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull @CallbackExecutor Executor executor, @NonNull WifiConnectedNetworkScorer scorer) { if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (scorer == null) throw new IllegalArgumentException("scorer cannot be null"); if (mVerboseLoggingEnabled) { Log.v(TAG, "setWifiConnectedNetworkScorer: scorer=" + scorer); } try { return mService.setWifiConnectedNetworkScorer(new Binder(), new WifiConnectedNetworkScorerProxy(executor, scorer)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow caller to clear a previously set scorer. After calling this method, * client will no longer receive information about start and stop of Wi-Fi connection. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void clearWifiConnectedNetworkScorer() { if (mVerboseLoggingEnabled) { Log.v(TAG, "clearWifiConnectedNetworkScorer"); } try { mService.clearWifiConnectedNetworkScorer(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable/disable wifi scan throttling from 3rd party apps. * *

    * The throttling limits for apps are described in * * https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling *

    * * @param enable true to allow scan throttling, false to disallow scan throttling. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean enable) { try { mService.setScanThrottleEnabled(enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the persisted Wi-Fi scan throttle state. Defaults to true, unless changed by the user via * Developer options. * *

    * The throttling limits for apps are described in * * https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling *

    * * @return true to indicate that scan throttling is enabled, false to indicate that scan * throttling is disabled. */ @RequiresPermission(ACCESS_WIFI_STATE) public boolean isScanThrottleEnabled() { try { return mService.isScanThrottleEnabled(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable/disable wifi auto wakeup feature. * *

    * The feature is described in * * https://source.android.com/devices/tech/connect/wifi-infrastructure * #turn_on_wi-fi_automatically * * * @param enable true to enable, false to disable. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean enable) { try { mService.setAutoWakeupEnabled(enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the persisted Wi-Fi auto wakeup feature state. Defaults to false, unless changed by the * user via Settings. * *

    * The feature is described in * * https://source.android.com/devices/tech/connect/wifi-infrastructure * #turn_on_wi-fi_automatically * * * @return true to indicate that wakeup feature is enabled, false to indicate that wakeup * feature is disabled. */ @RequiresPermission(ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled() { try { return mService.isAutoWakeupEnabled(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Sets the state of carrier offload on merged or unmerged networks for specified subscription. * *

    * When a subscription's carrier network offload is disabled, all network suggestions related to * this subscription will not be considered for auto join. *

    * If calling app want disable all carrier network offload from a specified subscription, should * call this API twice to disable both merged and unmerged carrier network suggestions. * * @param subscriptionId See {@link SubscriptionInfo#getSubscriptionId()}. * @param merged True for carrier merged network, false otherwise. * See {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} * @param enabled True for enable carrier network offload, false otherwise. * @see #isCarrierNetworkOffloadEnabled(int, boolean) * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged, boolean enabled) { try { mService.setCarrierNetworkOffloadEnabled(subscriptionId, merged, enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the carrier network offload state for merged or unmerged networks for specified * subscription. * @param subscriptionId subscription ID see {@link SubscriptionInfo#getSubscriptionId()} * @param merged True for carrier merged network, false otherwise. * See {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} * @return True to indicate that carrier network offload is enabled, false otherwise. */ @RequiresPermission(ACCESS_WIFI_STATE) public boolean isCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged) { try { return mService.isCarrierNetworkOffloadEnabled(subscriptionId, merged); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Interface for network suggestion user approval status change listener. * Should be implemented by applications and registered using * {@link #addSuggestionUserApprovalStatusListener(Executor, * SuggestionUserApprovalStatusListener)} ( */ public interface SuggestionUserApprovalStatusListener { /** * Called when the user approval status of the App has changed. * @param status The current status code for the user approval. One of the * {@code STATUS_SUGGESTION_APPROVAL_} values. */ void onUserApprovalStatusChange(@SuggestionUserApprovalStatus int status); } private class SuggestionUserApprovalStatusListenerProxy extends ISuggestionUserApprovalStatusListener.Stub { private final Executor mExecutor; private final SuggestionUserApprovalStatusListener mListener; SuggestionUserApprovalStatusListenerProxy(@NonNull Executor executor, @NonNull SuggestionUserApprovalStatusListener listener) { mExecutor = executor; mListener = listener; } @Override public void onUserApprovalStatusChange(int status) { mExecutor.execute(() -> mListener.onUserApprovalStatusChange(status)); } } /** * Add a listener for Wi-Fi network suggestion user approval status. * See {@link SuggestionUserApprovalStatusListener}. * Caller will receive a callback immediately after adding a listener and when the user approval * status of the caller has changed. * Caller can remove a previously registered listener using * {@link WifiManager#removeSuggestionUserApprovalStatusListener( * SuggestionUserApprovalStatusListener)} * A caller can add multiple listeners to monitor the event. * @param executor The executor to execute the listener of the {@code listener} object. * @param listener listener for suggestion user approval status changes. */ @RequiresPermission(ACCESS_WIFI_STATE) public void addSuggestionUserApprovalStatusListener( @NonNull @CallbackExecutor Executor executor, @NonNull SuggestionUserApprovalStatusListener listener) { if (listener == null) throw new NullPointerException("Listener cannot be null"); if (executor == null) throw new NullPointerException("Executor cannot be null"); Log.v(TAG, "addSuggestionUserApprovalStatusListener listener=" + listener + ", executor=" + executor); try { synchronized (sSuggestionUserApprovalStatusListenerMap) { ISuggestionUserApprovalStatusListener.Stub binderCallback = new SuggestionUserApprovalStatusListenerProxy(executor, listener); sSuggestionUserApprovalStatusListenerMap.put(System.identityHashCode(listener), binderCallback); mService.addSuggestionUserApprovalStatusListener(binderCallback, mContext.getOpPackageName()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allow callers to remove a previously registered listener using * {@link #addSuggestionUserApprovalStatusListener(Executor, * SuggestionUserApprovalStatusListener)}. After calling this method, * applications will no longer receive network suggestion user approval status change through * that listener. */ @RequiresPermission(ACCESS_WIFI_STATE) public void removeSuggestionUserApprovalStatusListener( @NonNull SuggestionUserApprovalStatusListener listener) { if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); Log.v(TAG, "removeSuggestionUserApprovalStatusListener: listener=" + listener); try { synchronized (sSuggestionUserApprovalStatusListenerMap) { int listenerIdentifier = System.identityHashCode(listener); if (!sSuggestionUserApprovalStatusListenerMap.contains(listenerIdentifier)) { Log.w(TAG, "Unknown external callback " + listenerIdentifier); return; } mService.removeSuggestionUserApprovalStatusListener( sSuggestionUserApprovalStatusListenerMap.get(listenerIdentifier), mContext.getOpPackageName()); sSuggestionUserApprovalStatusListenerMap.remove(listenerIdentifier); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Indicates the start/end of an emergency scan request being processed by {@link WifiScanner}. * The wifi stack should ensure that the wifi chip remains on for the duration of the scan. * WifiScanner detects emergency scan requests via * {@link WifiScanner.ScanSettings#ignoreLocationSettings} flag. * * If the wifi stack is off (because location & wifi toggles are off) when this indication is * received, the wifi stack will temporarily move to a scan only mode. Since location toggle * is off, only scan with * {@link WifiScanner.ScanSettings#ignoreLocationSettings} flag set will be * allowed to be processed for this duration. * * @hide */ @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setEmergencyScanRequestInProgress(boolean inProgress) { try { mService.setEmergencyScanRequestInProgress(inProgress); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable or disable Wi-Fi scoring. Wi-Fi network status is evaluated by Wi-Fi scoring * {@link WifiScoreReport}. This API enables/disables Wi-Fi scoring to take action on network * selection. * * @param enabled {@code true} to enable, {@code false} to disable. * @return true The status of Wifi scoring is set successfully. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setWifiScoringEnabled(boolean enabled) { if (mVerboseLoggingEnabled) { Log.v(TAG, "setWifiScoringEnabled: " + enabled); } try { return mService.setWifiScoringEnabled(enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Flush Passpoint ANQP cache, and clear pending ANQP requests. Allows the caller to reset the * Passpoint ANQP state, if required. * * Notes: * 1. Flushing the ANQP cache may cause delays in discovering and connecting to Passpoint * networks. * 2. Intended to be used by Device Owner (DO), Profile Owner (PO), Settings and provisioning * apps. */ @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_MANAGED_PROVISIONING, android.Manifest.permission.NETWORK_CARRIER_PROVISIONING }, conditional = true) public void flushPasspointAnqpCache() { try { mService.flushPasspointAnqpCache(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a list of {@link WifiAvailableChannel} for the specified band and operational * mode(s), that is allowed for the current regulatory domain. An empty list implies that there * are no available channels for use. * * Note: the {@code band} parameter which is specified as a {@code WifiScanner#WIFI_BAND_*} * constant is limited to one of the band values specified below. Specifically, if the 5GHz * band is included then it must include the DFS channels - an exception will be thrown * otherwise. The caller should not make any assumptions about whether DFS channels are allowed. * This API will indicate whether DFS channels are allowed for the specified operation mode(s) * per device policy. * * @param band one of the following band constants defined in {@code WifiScanner#WIFI_BAND_*} * constants. * 1. {@code WifiScanner#WIFI_BAND_UNSPECIFIED}=0 - no band specified; Looks for the * channels in all the available bands - 2.4 GHz, 5 GHz, 6 GHz and 60 GHz * 2. {@code WifiScanner#WIFI_BAND_24_GHZ}=1 * 3. {@code WifiScanner#WIFI_BAND_5_GHZ_WITH_DFS}=6 * 4. {@code WifiScanner#WIFI_BAND_BOTH_WITH_DFS}=7 * 5. {@code WifiScanner#WIFI_BAND_6_GHZ}=8 * 6. {@code WifiScanner#WIFI_BAND_24_5_WITH_DFS_6_GHZ}=15 * 7. {@code WifiScanner#WIFI_BAND_60_GHZ}=16 * 8. {@code WifiScanner#WIFI_BAND_24_5_WITH_DFS_6_60_GHZ}=31 * @param mode Bitwise OR of {@code WifiAvailableChannel#OP_MODE_*} constants * e.g. {@link WifiAvailableChannel#OP_MODE_WIFI_AWARE} * @return a list of {@link WifiAvailableChannel} * * @throws UnsupportedOperationException - if this API is not supported on this device * or IllegalArgumentException - if the band specified is not one among the list * of bands mentioned above. */ @RequiresApi(Build.VERSION_CODES.S) @NonNull @RequiresPermission(NEARBY_WIFI_DEVICES) public List getAllowedChannels( int band, @WifiAvailableChannel.OpMode int mode) { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } return mService.getUsableChannels(band, mode, WifiAvailableChannel.FILTER_REGULATORY, mContext.getOpPackageName(), extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a list of {@link WifiAvailableChannel} for the specified band and operational * mode(s) per the current regulatory domain and device-specific constraints such as concurrency * state and interference due to other radios. An empty list implies that there are no available * channels for use. * * Note: the {@code band} parameter which is specified as a {@code WifiScanner#WIFI_BAND_*} * constant is limited to one of the band values specified below. Specifically, if the 5GHz * band is included then it must include the DFS channels - an exception will be thrown * otherwise. The caller should not make any assumptions about whether DFS channels are allowed. * This API will indicate whether DFS channels are allowed for the specified operation mode(s) * per device policy. * * @param band one of the following band constants defined in {@code WifiScanner#WIFI_BAND_*} * constants. * 1. {@code WifiScanner#WIFI_BAND_UNSPECIFIED}=0 - no band specified; Looks for the * channels in all the available bands - 2.4 GHz, 5 GHz, 6 GHz and 60 GHz * 2. {@code WifiScanner#WIFI_BAND_24_GHZ}=1 * 3. {@code WifiScanner#WIFI_BAND_5_GHZ_WITH_DFS}=6 * 4. {@code WifiScanner#WIFI_BAND_BOTH_WITH_DFS}=7 * 5. {@code WifiScanner#WIFI_BAND_6_GHZ}=8 * 6. {@code WifiScanner#WIFI_BAND_24_5_WITH_DFS_6_GHZ}=15 * 7. {@code WifiScanner#WIFI_BAND_60_GHZ}=16 * 8. {@code WifiScanner#WIFI_BAND_24_5_WITH_DFS_6_60_GHZ}=31 * @param mode Bitwise OR of {@code WifiAvailableChannel#OP_MODE_*} constants * e.g. {@link WifiAvailableChannel#OP_MODE_WIFI_AWARE} * @return a list of {@link WifiAvailableChannel} * * @throws UnsupportedOperationException - if this API is not supported on this device * or IllegalArgumentException - if the band specified is not one among the list * of bands mentioned above. */ @RequiresApi(Build.VERSION_CODES.S) @NonNull @RequiresPermission(NEARBY_WIFI_DEVICES) public List getUsableChannels( int band, @WifiAvailableChannel.OpMode int mode) { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } return mService.getUsableChannels(band, mode, WifiAvailableChannel.getUsableFilter(), mContext.getOpPackageName(), extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * If the device supports Wi-Fi Passpoint, the user can explicitly enable or disable it. * That status can be queried using this method. * @return {@code true} if Wi-Fi Passpoint is enabled * */ @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiPasspointEnabled() { try { return mService.isWifiPasspointEnabled(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Explicitly enable or disable Wi-Fi Passpoint as a global switch. * The global Passpoint enabling/disabling overrides individual configuration * enabling/disabling. * Passpoint global status can be queried by {@link WifiManager#isWifiPasspointEnabled }. * * @param enabled {@code true} to enable, {@code false} to disable. * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) public void setWifiPasspointEnabled(boolean enabled) { if (mVerboseLoggingEnabled) { Log.v(TAG, "setWifiPasspointEnabled: " + enabled); } try { mService.setWifiPasspointEnabled(enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * The device may support concurrent connections to multiple internet-providing Wi-Fi * networks (APs) - that is indicated by * {@link WifiManager#isStaConcurrencyForMultiInternetSupported()}. * This method indicates whether or not the feature is currently enabled. * A value of {@link WifiManager#WIFI_MULTI_INTERNET_MODE_DISABLED} indicates that the feature * is disabled, a value of {@link WifiManager#WIFI_MULTI_INTERNET_MODE_DBS_AP} or * {@link WifiManager#WIFI_MULTI_INTERNET_MODE_MULTI_AP} indicates that the feature is enabled. * * The app can register to receive the corresponding Wi-Fi networks using the * {@link ConnectivityManager#registerNetworkCallback(NetworkRequest, NetworkCallback)} API with * a {@link WifiNetworkSpecifier} configured using the * {@link WifiNetworkSpecifier.Builder#setBand(int)} method. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public @WifiMultiInternetMode int getStaConcurrencyForMultiInternetMode() { try { return mService.getStaConcurrencyForMultiInternetMode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if the currently connected network meets the minimum required Wi-Fi security level set. * If not, the current network will be disconnected. * * @throws SecurityException if the caller does not have permission. * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void notifyMinimumRequiredWifiSecurityLevelChanged(int level) { if (mVerboseLoggingEnabled) { Log.v(TAG, "notifyMinimumRequiredWifiSecurityLevelChanged"); } try { mService.notifyMinimumRequiredWifiSecurityLevelChanged(level); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Check if the currently connected network meets the Wi-Fi SSID policy set. * If not, the current network will be disconnected. * * @throws SecurityException if the caller does not have permission. * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void notifyWifiSsidPolicyChanged(@NonNull WifiSsidPolicy policy) { if (mVerboseLoggingEnabled) { Log.v(TAG, "notifyWifiSsidPolicyChanged"); } try { if (policy != null) { mService.notifyWifiSsidPolicyChanged( policy.getPolicyType(), new ArrayList<>(policy.getSsids())); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Configure whether or not concurrent multiple connections to internet-providing Wi-Fi * networks (AP) is enabled. * Use {@link WifiManager#WIFI_MULTI_INTERNET_MODE_DISABLED} to disable, and either * {@link WifiManager#WIFI_MULTI_INTERNET_MODE_DBS_AP} or * {@link WifiManager#WIFI_MULTI_INTERNET_MODE_MULTI_AP} to enable in different modes. * The {@link WifiManager#getStaConcurrencyForMultiInternetMode() } can be used to retrieve * the current mode. * * @param mode Multi internet mode. * @return true when the mode is set successfully, false when failed. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) public boolean setStaConcurrencyForMultiInternetMode(@WifiMultiInternetMode int mode) { if (mVerboseLoggingEnabled) { Log.v(TAG, "setStaConcurrencyForMultiInternetMode: " + mode); } try { return mService.setStaConcurrencyForMultiInternetMode(mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Intent action to launch a dialog from the WifiDialog app. * Must include EXTRA_DIALOG_ID, EXTRA_DIALOG_TYPE, and appropriate extras for the dialog type. * @hide */ public static final String ACTION_LAUNCH_DIALOG = "android.net.wifi.action.LAUNCH_DIALOG"; /** * Intent action to dismiss an existing dialog from the WifiDialog app. * Must include EXTRA_DIALOG_ID. * @hide */ public static final String ACTION_DISMISS_DIALOG = "android.net.wifi.action.DISMISS_DIALOG"; /** * Unknown DialogType. * @hide */ public static final int DIALOG_TYPE_UNKNOWN = 0; /** * DialogType for a simple dialog. * @see {@link com.android.server.wifi.WifiDialogManager#createSimpleDialog} * @hide */ public static final int DIALOG_TYPE_SIMPLE = 1; /** * DialogType for a P2P Invitation Sent dialog. * @see {@link com.android.server.wifi.WifiDialogManager#createP2pInvitationSentDialog} * @hide */ public static final int DIALOG_TYPE_P2P_INVITATION_SENT = 2; /** * DialogType for a P2P Invitation Received dialog. * @see {@link com.android.server.wifi.WifiDialogManager#createP2pInvitationReceivedDialog} * @hide */ public static final int DIALOG_TYPE_P2P_INVITATION_RECEIVED = 3; /** @hide */ @IntDef(prefix = { "DIALOG_TYPE_" }, value = { DIALOG_TYPE_UNKNOWN, DIALOG_TYPE_SIMPLE, DIALOG_TYPE_P2P_INVITATION_SENT, DIALOG_TYPE_P2P_INVITATION_RECEIVED, }) @Retention(RetentionPolicy.SOURCE) public @interface DialogType {} /** * Dialog positive button was clicked. * @hide */ public static final int DIALOG_REPLY_POSITIVE = 0; /** * Dialog negative button was clicked. * @hide */ public static final int DIALOG_REPLY_NEGATIVE = 1; /** * Dialog neutral button was clicked. * @hide */ public static final int DIALOG_REPLY_NEUTRAL = 2; /** * Dialog was cancelled. * @hide */ public static final int DIALOG_REPLY_CANCELLED = 3; /** * Indication of a reply to a dialog. * See {@link WifiManager#replyToSimpleDialog(int, int)} * @hide */ @IntDef(prefix = { "DIALOG_TYPE_" }, value = { DIALOG_REPLY_POSITIVE, DIALOG_REPLY_NEGATIVE, DIALOG_REPLY_NEUTRAL, DIALOG_REPLY_CANCELLED, }) @Retention(RetentionPolicy.SOURCE) public @interface DialogReply {} /** * Invalid dialog id for dialogs that are not currently active. * @hide */ public static final int INVALID_DIALOG_ID = -1; /** * Extra int indicating the type of dialog to display. * @hide */ public static final String EXTRA_DIALOG_TYPE = "android.net.wifi.extra.DIALOG_TYPE"; /** * Extra int indicating the ID of a dialog. The value must not be {@link #INVALID_DIALOG_ID}. * @hide */ public static final String EXTRA_DIALOG_ID = "android.net.wifi.extra.DIALOG_ID"; /** * Extra String indicating the title of a simple dialog. * @hide */ public static final String EXTRA_DIALOG_TITLE = "android.net.wifi.extra.DIALOG_TITLE"; /** * Extra String indicating the message of a simple dialog. * @hide */ public static final String EXTRA_DIALOG_MESSAGE = "android.net.wifi.extra.DIALOG_MESSAGE"; /** * Extra String indicating the message URL of a simple dialog. * @hide */ public static final String EXTRA_DIALOG_MESSAGE_URL = "android.net.wifi.extra.DIALOG_MESSAGE_URL"; /** * Extra String indicating the start index of a message URL span of a simple dialog. * @hide */ public static final String EXTRA_DIALOG_MESSAGE_URL_START = "android.net.wifi.extra.DIALOG_MESSAGE_URL_START"; /** * Extra String indicating the end index of a message URL span of a simple dialog. * @hide */ public static final String EXTRA_DIALOG_MESSAGE_URL_END = "android.net.wifi.extra.DIALOG_MESSAGE_URL_END"; /** * Extra String indicating the positive button text of a simple dialog. * @hide */ public static final String EXTRA_DIALOG_POSITIVE_BUTTON_TEXT = "android.net.wifi.extra.DIALOG_POSITIVE_BUTTON_TEXT"; /** * Extra String indicating the negative button text of a simple dialog. * @hide */ public static final String EXTRA_DIALOG_NEGATIVE_BUTTON_TEXT = "android.net.wifi.extra.DIALOG_NEGATIVE_BUTTON_TEXT"; /** * Extra String indicating the neutral button text of a simple dialog. * @hide */ public static final String EXTRA_DIALOG_NEUTRAL_BUTTON_TEXT = "android.net.wifi.extra.DIALOG_NEUTRAL_BUTTON_TEXT"; /** * Extra long indicating the timeout in milliseconds of a dialog. * @hide */ public static final String EXTRA_DIALOG_TIMEOUT_MS = "android.net.wifi.extra.DIALOG_TIMEOUT_MS"; /** * Extra String indicating a P2P device name for a P2P Invitation Sent/Received dialog. * @hide */ public static final String EXTRA_P2P_DEVICE_NAME = "android.net.wifi.extra.P2P_DEVICE_NAME"; /** * Extra boolean indicating that a PIN is requested for a P2P Invitation Received dialog. * @hide */ public static final String EXTRA_P2P_PIN_REQUESTED = "android.net.wifi.extra.P2P_PIN_REQUESTED"; /** * Extra String indicating the PIN to be displayed for a P2P Invitation Sent/Received dialog. * @hide */ public static final String EXTRA_P2P_DISPLAY_PIN = "android.net.wifi.extra.P2P_DISPLAY_PIN"; /** * Extra boolean indicating ACTION_CLOSE_SYSTEM_DIALOGS should not close the Wi-Fi dialogs. * @hide */ public static final String EXTRA_CLOSE_SYSTEM_DIALOGS_EXCEPT_WIFI = "android.net.wifi.extra.CLOSE_SYSTEM_DIALOGS_EXCEPT_WIFI"; /** * Returns a set of packages that aren't DO or PO but should be able to manage WiFi networks. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) @NonNull public Set getOemPrivilegedWifiAdminPackages() { try { return new ArraySet<>(mService.getOemPrivilegedWifiAdminPackages()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Method for WifiDialog to notify the framework of a reply to a simple dialog. * @param dialogId id of the replying dialog. * @param reply reply of the dialog. * @hide */ public void replyToSimpleDialog(int dialogId, @DialogReply int reply) { if (mVerboseLoggingEnabled) { Log.v(TAG, "replyToWifiEnableRequestDialog: dialogId=" + dialogId + " reply=" + reply); } try { mService.replyToSimpleDialog(dialogId, reply); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Method for WifiDialog to notify the framework of a reply to a P2P Invitation Received dialog. * @param dialogId id of the replying dialog. * @param accepted Whether the invitation was accepted. * @param optionalPin PIN of the reply, or {@code null} if none was supplied. * @hide */ public void replyToP2pInvitationReceivedDialog( int dialogId, boolean accepted, @Nullable String optionalPin) { if (mVerboseLoggingEnabled) { Log.v(TAG, "replyToP2pInvitationReceivedDialog: " + "dialogId=" + dialogId + ", accepted=" + accepted + ", pin=" + optionalPin); } try { mService.replyToP2pInvitationReceivedDialog(dialogId, accepted, optionalPin); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Specify a list of DHCP options to use for any network whose SSID is specified and which * transmits vendor-specific information elements (VSIEs) using the specified Organizationally * Unique Identifier (OUI). If the AP transmits VSIEs for multiple specified OUIs then all * matching DHCP options will be used. The allowlist for DHCP options in * {@link android.net.ip.IpClient} gates whether the DHCP options will actually be used. * When DHCP options are used: if the option value {@link android.net.DhcpOption#getValue()} * is null, the option type {@link android.net.DhcpOption#getType()} will be put in the * Parameter Request List in the DHCP packets; otherwise, the option will be included in the * options section in the DHCP packets. Use {@link #removeCustomDhcpOptions(Object, Object)} * to remove the specified DHCP options. * * @param ssid the network SSID. * @param oui the 3-byte OUI. * @param options the list of {@link android.net.DhcpOption}. * * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public void addCustomDhcpOptions(@NonNull WifiSsid ssid, @NonNull byte[] oui, @NonNull List options) { if (mVerboseLoggingEnabled) { Log.v(TAG, "addCustomDhcpOptions: ssid=" + ssid + ", oui=" + Arrays.toString(oui) + ", options=" + options); } try { mService.addCustomDhcpOptions(ssid, oui, options); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove custom DHCP options specified by {@link #addCustomDhcpOptions(Object, Object, List)}. * * @param ssid the network SSID. * @param oui the 3-byte OUI. * * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public void removeCustomDhcpOptions(@NonNull WifiSsid ssid, @NonNull byte[] oui) { if (mVerboseLoggingEnabled) { Log.v(TAG, "removeCustomDhcpOptions: ssid=" + ssid + ", oui=" + Arrays.toString(oui)); } try { mService.removeCustomDhcpOptions(ssid, oui); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Wi-Fi interface of type STA (station/client Wi-Fi infrastructure device). */ public static final int WIFI_INTERFACE_TYPE_STA = 0; /** * Wi-Fi interface of type AP (access point Wi-Fi infrastructure device). */ public static final int WIFI_INTERFACE_TYPE_AP = 1; /** * Wi-Fi interface of type Wi-Fi Aware (aka NAN). */ public static final int WIFI_INTERFACE_TYPE_AWARE = 2; /** * Wi-Fi interface of type Wi-Fi Direct (aka P2P). */ public static final int WIFI_INTERFACE_TYPE_DIRECT = 3; /** @hide */ @IntDef(prefix = { "WIFI_INTERFACE_TYPE_" }, value = { WIFI_INTERFACE_TYPE_STA, WIFI_INTERFACE_TYPE_AP, WIFI_INTERFACE_TYPE_AWARE, WIFI_INTERFACE_TYPE_DIRECT, }) @Retention(RetentionPolicy.SOURCE) public @interface WifiInterfaceType {} /** * Class describing an impact of interface creation - returned by * {@link #reportCreateInterfaceImpact(int, boolean, Executor, BiConsumer)}. Due to Wi-Fi * concurrency limitations certain interfaces may have to be torn down. Each of these * interfaces was requested by a set of applications who could potentially be impacted. * * This class contain the information for a single interface: the interface type with * {@link InterfaceCreationImpact#getInterfaceType()} and the set of impacted packages * with {@link InterfaceCreationImpact#getPackages()}. */ public static class InterfaceCreationImpact { private final int mInterfaceType; private final Set mPackages; public InterfaceCreationImpact(@WifiInterfaceType int interfaceType, @NonNull Set packages) { mInterfaceType = interfaceType; mPackages = packages; } /** * @return The interface type which will be torn down to make room for the interface * requested in {@link #reportCreateInterfaceImpact(int, boolean, Executor, BiConsumer)}. */ public @WifiInterfaceType int getInterfaceType() { return mInterfaceType; } /** * @return The list of potentially impacted packages due to tearing down the interface * specified in {@link #getInterfaceType()}. */ public @NonNull Set getPackages() { return mPackages; } @Override public int hashCode() { return Objects.hash(mInterfaceType, mPackages); } @Override public boolean equals(Object that) { if (this == that) return true; if (!(that instanceof InterfaceCreationImpact)) return false; InterfaceCreationImpact thatInterfaceCreationImpact = (InterfaceCreationImpact) that; return this.mInterfaceType == thatInterfaceCreationImpact.mInterfaceType && Objects.equals(this.mPackages, thatInterfaceCreationImpact.mPackages); } } /** * Queries the framework to determine whether the specified interface can be created, and if * so - what other interfaces would be torn down by the framework to allow this creation if * it were requested. The result is returned via the specified {@link BiConsumer} callback * which returns two arguments: *

  • First argument: a {@code boolean} - indicating whether or not the interface can be * created.
  • *
  • Second argument: a {@code Set} - if the interface can be * created (first argument is {@code true} then this is the set of interface types which * will be removed and the packages which requested them. Possibly an empty set. If the * first argument is {@code false}, then an empty set will be returned here.
  • *

    * Interfaces, input and output, are specified using the {@code WIFI_INTERFACE_*} constants: * {@link #WIFI_INTERFACE_TYPE_STA}, {@link #WIFI_INTERFACE_TYPE_AP}, * {@link #WIFI_INTERFACE_TYPE_AWARE}, or {@link #WIFI_INTERFACE_TYPE_DIRECT}. *

    * This method does not actually create the interface. That operation is handled by the * framework when a particular service method is called. E.g. a Wi-Fi Direct interface may be * created when various methods of {@link android.net.wifi.p2p.WifiP2pManager} are called, * similarly for Wi-Fi Aware and {@link android.net.wifi.aware.WifiAwareManager}. *

    * Note: the information returned via this method is the current snapshot of the system. It may * change due to actions of the framework or other apps. * * @param interfaceType The interface type whose possible creation is being queried. * @param requireNewInterface Indicates that the query is for a new interface of the specified * type - an existing interface won't meet the query. Some * operations (such as Wi-Fi Direct and Wi-Fi Aware are a shared * resource and so may not need a new interface). * @param executor An {@link Executor} on which to return the result. * @param resultCallback The asynchronous callback which will return two argument: a * {@code boolean} (whether the interface can be created), and a * {@code Set} (a set of {@link InterfaceCreationImpact}: * interfaces which will be destroyed when the interface is created * and the packages which requested them and thus may be impacted). */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(allOf = {android.Manifest.permission.MANAGE_WIFI_INTERFACES, ACCESS_WIFI_STATE}) public void reportCreateInterfaceImpact(@WifiInterfaceType int interfaceType, boolean requireNewInterface, @NonNull @CallbackExecutor Executor executor, @NonNull BiConsumer> resultCallback) { Objects.requireNonNull(executor, "Non-null executor required"); Objects.requireNonNull(resultCallback, "Non-null resultCallback required"); try { mService.reportCreateInterfaceImpact(mContext.getOpPackageName(), interfaceType, requireNewInterface, new IInterfaceCreationInfoCallback.Stub() { @Override public void onResults(boolean canCreate, int[] interfacesToDelete, String[] packagesForInterfaces) { Binder.clearCallingIdentity(); if ((interfacesToDelete == null && packagesForInterfaces != null) || (interfacesToDelete != null && packagesForInterfaces == null) || (canCreate && ( interfacesToDelete == null || interfacesToDelete.length != packagesForInterfaces.length))) { Log.e(TAG, "reportImpactToCreateIfaceRequest: Invalid callback " + "parameters - canCreate=" + canCreate + ", interfacesToDelete=" + Arrays.toString(interfacesToDelete) + ", worksourcesForInterfaces=" + Arrays.toString(packagesForInterfaces)); return; } final Set finalSet = (canCreate && interfacesToDelete.length > 0) ? new ArraySet<>() : Collections.emptySet(); if (canCreate) { for (int i = 0; i < interfacesToDelete.length; ++i) { finalSet.add( new InterfaceCreationImpact(interfacesToDelete[i], packagesForInterfaces[i] == null ? Collections.emptySet() : new ArraySet<>( packagesForInterfaces[i] .split(",")))); } } executor.execute(() -> resultCallback.accept(canCreate, finalSet)); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns the max number of channels that is allowed to be set on a * {@link WifiNetworkSpecifier}. * @see WifiNetworkSpecifier.Builder#setPreferredChannelsFrequenciesMhz(int[]) * * @return The max number of channels can be set on a request. */ public int getMaxNumberOfChannelsPerNetworkSpecifierRequest() { try { return mService.getMaxNumberOfChannelsPerRequest(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Add a list of new application-initiated QoS policies. * * Note: Policies are managed using a policy ID, which can be retrieved using * {@link QosPolicyParams#getPolicyId()}. This ID can be used when removing a policy via * {@link #removeQosPolicies(int[])}. The caller is in charge of assigning and managing * the policy IDs for any requested policies. * * Note: Policies with duplicate IDs are not allowed. To update an existing policy, first * remove it using {@link #removeQosPolicies(int[])}, and then re-add it using this API. * * Note: All policies in a single request must have the same {@link QosPolicyParams.Direction}. * * Note: Support for the {@link QosPolicyParams#DIRECTION_UPLINK} direction is added in * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}. For earlier releases, * only the {@link QosPolicyParams#DIRECTION_DOWNLINK} direction is supported. * * @param policyParamsList List of {@link QosPolicyParams} objects describing the requested * policies. Must have a maximum length of * {@link #getMaxNumberOfPoliciesPerQosRequest()}. * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return a list of integer status * codes from {@link QosRequestStatus}. Result list will be the same * length as the input list, and each status code will correspond to * the policy at that index in the input list. * * @throws SecurityException if caller does not have the required permissions. * @throws NullPointerException if the caller provided a null input. * @throws UnsupportedOperationException if the feature is not enabled. * @throws IllegalArgumentException if the input list is invalid. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION }) public void addQosPolicies(@NonNull List policyParamsList, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer> resultsCallback) { Objects.requireNonNull(policyParamsList, "policyParamsList cannot be null"); Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.addQosPolicies(policyParamsList, new Binder(), mContext.getOpPackageName(), new IListListener.Stub() { @Override public void onResult(List value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove a list of existing application-initiated QoS policies, previously added via * {@link #addQosPolicies(List, Executor, Consumer)}. * * Note: Policies are identified by their policy IDs, which are assigned by the caller. The ID * for a given policy can be retrieved using {@link QosPolicyParams#getPolicyId()}. * * @param policyIdList List of policy IDs corresponding to the policies to remove. Must have * a maximum length of {@link #getMaxNumberOfPoliciesPerQosRequest()}. * @throws SecurityException if caller does not have the required permissions. * @throws NullPointerException if the caller provided a null input. * @throws IllegalArgumentException if the input list is invalid. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION }) public void removeQosPolicies(@NonNull int[] policyIdList) { Objects.requireNonNull(policyIdList, "policyIdList cannot be null"); try { mService.removeQosPolicies(policyIdList, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Remove all application-initiated QoS policies requested by this caller, * previously added via {@link #addQosPolicies(List, Executor, Consumer)}. * * @throws SecurityException if caller does not have the required permissions. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION }) public void removeAllQosPolicies() { try { mService.removeAllQosPolicies(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Set the link layer stats polling interval, in milliseconds. * * @param intervalMs a non-negative integer, for the link layer stats polling interval * in milliseconds. * To set a fixed interval, use a positive value. * For automatic handling of the interval, use value 0 * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws SecurityException if the caller does not have permission. * @throws IllegalArgumentException if input is invalid. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void setLinkLayerStatsPollingInterval(@IntRange (from = 0) int intervalMs) { try { mService.setLinkLayerStatsPollingInterval(intervalMs); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the link layer stats polling interval, in milliseconds. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return current * link layer stats polling interval in milliseconds. * * @throws UnsupportedOperationException if the API is not supported on this SDK version. * @throws SecurityException if the caller does not have permission. * @throws NullPointerException if the caller provided invalid inputs. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void getLinkLayerStatsPollingInterval(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.getLinkLayerStatsPollingInterval( new IIntegerListener.Stub() { @Override public void onResult(int value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This API allows a privileged application to set Multi-Link Operation mode. * * Multi-link operation (MLO) will allow Wi-Fi devices to operate on multiple links at the same * time through a single connection, aiming to support applications that require lower latency, * and higher capacity. Chip vendors have algorithms that run on the chip to use available links * based on incoming traffic and various inputs. This API allows system application to give a * suggestion to such algorithms on its preference using {@link MloMode}. * * * @param mode Refer {@link MloMode} for supported modes. * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return {@code Boolean} indicating * whether the MLO mode is successfully set or not. * @throws IllegalArgumentException if mode value is not in {@link MloMode}. * @throws NullPointerException if the caller provided a null input. * @throws SecurityException if caller does not have the required permissions. * @throws UnsupportedOperationException if the set operation is not supported on this SDK. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) public void setMloMode(@MloMode int mode, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { if (mode < MLO_MODE_DEFAULT || mode > MLO_MODE_LOW_POWER) { throw new IllegalArgumentException("invalid mode: " + mode); } Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.setMloMode(mode, new IBooleanListener.Stub() { @Override public void onResult(boolean value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This API allows a privileged application to get Multi-Link Operation mode. Refer * {@link WifiManager#setMloMode(int, Executor, Consumer)} for more details. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return current MLO mode. Returns * {@link MloMode#MLO_MODE_DEFAULT} if information is not available, * e.g. if the driver/firmware doesn't provide this information. * @throws NullPointerException if the caller provided a null input. * @throws SecurityException if caller does not have the required permissions. * @throws UnsupportedOperationException if the get operation is not supported on this SDK. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) public void getMloMode(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.getMloMode(new IIntegerListener.Stub() { @Override public void onResult(int value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the maximum number of links supported by the chip for MLO association. e.g. if the Wi-Fi * chip supports eMLSR (Enhanced Multi-Link Single Radio) and STR (Simultaneous Transmit and * Receive) with following capabilities, * - Max MLO assoc link count = 3. * - Max MLO STR link count = 2. See * {@link WifiManager#getMaxMloStrLinkCount(Executor, Consumer)} * One of the possible configuration is - STR (2.4 GHz , eMLSR(5 GHz, 6 GHz)), provided the * radio combination of the chip supports it. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return maximum MLO association link * count supported by the chip or -1 if error or not available. * @throws NullPointerException if the caller provided a null input. * @throws SecurityException if caller does not have the required permissions. * @throws UnsupportedOperationException if the get operation is not supported on this SDK. * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) public void getMaxMloAssociationLinkCount(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.getMaxMloAssociationLinkCount(new IIntegerListener.Stub() { @Override public void onResult(int value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }, extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the maximum number of STR links used in Multi-Link Operation. The maximum number of STR * links used for MLO can be different from the number of radios supported by the chip. e.g. if * the Wi-Fi chip supports eMLSR (Enhanced Multi-Link Single Radio) and STR (Simultaneous * Transmit and Receive) with following capabilities, * - Max MLO assoc link count = 3. See * {@link WifiManager#getMaxMloAssociationLinkCount(Executor, Consumer)}. * - Max MLO STR link count = 2. * One of the possible configuration is - STR (2.4 GHz, eMLSR(5 GHz, 6 GHz)), provided the radio * combination of the chip supports it. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return maximum STR link count * supported by the chip in MLO mode or -1 if error or not available. * @throws NullPointerException if the caller provided a null input. * @throws SecurityException if caller does not have the required permissions. * @throws UnsupportedOperationException if the get operation is not supported on this SDK * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) public void getMaxMloStrLinkCount(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.getMaxMloStrLinkCount(new IIntegerListener.Stub() { @Override public void onResult(int value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }, extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the set of band combinations supported simultaneously by the Wi-Fi Chip. * * Note: This method returns simultaneous band operation combination and not multichannel * concurrent operation (MCC) combination. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return a list of possible * simultaneous band combinations supported by the chip or empty list if * not available. Band value is defined in {@link WifiScanner.WifiBand}. * @throws NullPointerException if the caller provided a null input. * @throws SecurityException if caller does not have the required permissions. * @throws UnsupportedOperationException if the get operation is not supported on this SDK. * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) public void getSupportedSimultaneousBandCombinations( @NonNull @CallbackExecutor Executor executor, @NonNull Consumer> resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.getSupportedSimultaneousBandCombinations(new IWifiBandsListener.Stub() { @Override public void onResult(WifiBands[] supportedBands) { Binder.clearCallingIdentity(); List bandCombinations = new ArrayList<>(); for (WifiBands wifiBands : supportedBands) { bandCombinations.add(wifiBands.bands); } executor.execute(() -> { resultsCallback.accept(bandCombinations); }); } }, extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This API allows a privileged application to set whether or not this device allows * connections to Wi-Fi WEP networks. * * Note: The WEP connections may not work even if caller invokes this method with {@code true} * because device may NOT support connections to Wi-Fi WEP networks. * See: {@link #isWepSupported()}. * * @param isAllowed whether or not the user allow connections to Wi-Fi WEP networks. * @throws SecurityException if the caller does not have permission. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) public void setWepAllowed(boolean isAllowed) { try { mService.setWepAllowed(isAllowed); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Query whether or not this device is configured to allow connections to Wi-Fi WEP networks. * @see #setWepAllowed(boolean) * * Note: The WEP connections may not work even if this method returns {@code true} in the * result callback because device may NOT support connections to Wi-Fi WEP networks. * See: {@link #isWepSupported()}. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return {@code Boolean} indicating * whether wep network support is enabled/disabled. * * @throws SecurityException if the caller does not have permission. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) public void queryWepAllowed(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.queryWepAllowed( new IBooleanListener.Stub() { @Override public void onResult(boolean value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Enable Mirrored Stream Classification Service (MSCS) and configure using * the provided configuration values. * * If MSCS has already been enabled/configured, this will override the * existing configuration. * * Refer to Section 11.25.3 of the IEEE 802.11-2020 standard for more information. * * @param mscsParams {@link MscsParams} object containing the configuration parameters. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresPermission(anyOf = {MANAGE_WIFI_NETWORK_SELECTION}) public void enableMscs(@NonNull MscsParams mscsParams) { Objects.requireNonNull(mscsParams); try { mService.enableMscs(mscsParams); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Disable Mirrored Stream Classification Service (MSCS). * * If MSCS is enabled/configured, this will send a remove request to the AP. * * Refer to Section 11.25.3 of the IEEE 802.11-2020 standard for more information. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresPermission(anyOf = {MANAGE_WIFI_NETWORK_SELECTION}) public void disableMscs() { try { mService.disableMscs(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Do not send the DHCP hostname to open networks. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int FLAG_SEND_DHCP_HOSTNAME_RESTRICTION_OPEN = 1 << 0; /** * Do not send the DHCP hostname to secure network. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int FLAG_SEND_DHCP_HOSTNAME_RESTRICTION_SECURE = 1 << 1; /** @hide */ @IntDef(flag = true, prefix = { "FLAG_SEND_DHCP_HOSTNAME_RESTRICTION_" }, value = { FLAG_SEND_DHCP_HOSTNAME_RESTRICTION_OPEN, FLAG_SEND_DHCP_HOSTNAME_RESTRICTION_SECURE, }) @Retention(RetentionPolicy.SOURCE) public @interface SendDhcpHostnameRestriction {} /** * Sets the global restrictions on which networks to send the device hostname to during DHCP. * * @param restriction Bitmask of {@link SendDhcpHostnameRestriction}, or none to indicate no * restriction. * @throws IllegalArgumentException if input is invalid * @throws SecurityException if the calling app is not a Device Owner (DO), or a privileged app * that has one of the permissions required by this API. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) public void setSendDhcpHostnameRestriction(@SendDhcpHostnameRestriction int restriction) { try { mService.setSendDhcpHostnameRestriction(mContext.getOpPackageName(), restriction); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Query the global restriction on which networks to send the device hostname to during DHCP. * @see #setSendDhcpHostnameRestriction(int) * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return a bitmask of * {@link SendDhcpHostnameRestriction}. * * @throws SecurityException if the calling app is not a Device Owner (DO), or a privileged app * that has one of the permissions required by this API. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) public void querySendDhcpHostnameRestriction(@NonNull @CallbackExecutor Executor executor, @NonNull IntConsumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.querySendDhcpHostnameRestriction(mContext.getOpPackageName(), new IIntegerListener.Stub() { @Override public void onResult(int value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * @return true if this device supports Aggressive roaming mode * {@link #setPerSsidRoamingMode(WifiSsid, int)} */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public boolean isAggressiveRoamingModeSupported() { return isFeatureSupported(WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); } /** * This API allows a privileged application to set roaming mode per SSID. * * Available for DO/COPE apps. * Other apps require {@code android.Manifest.permission#NETWORK_SETTINGS} or * {@code android.Manifest.permission#MANAGE_WIFI_NETWORK_SELECTION} permission. * * @param ssid SSID to be mapped to apply roaming policy * @param roamingMode refer {@link RoamingMode} for supported modes. * @throws IllegalArgumentException if mode value is not in {@link RoamingMode}. * @throws NullPointerException if the caller provided a null input. * @throws SecurityException if caller does not have the required permission. * @throws UnsupportedOperationException if the set operation is not supported on this SDK or * if the feature is not available * {@link #isAggressiveRoamingModeSupported()}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @SuppressLint("RequiresPermission") public void setPerSsidRoamingMode(@NonNull WifiSsid ssid, @RoamingMode int roamingMode) { if (roamingMode < ROAMING_MODE_NONE || roamingMode > ROAMING_MODE_AGGRESSIVE) { throw new IllegalArgumentException("invalid roaming mode: " + roamingMode); } Objects.requireNonNull(ssid, "ssid cannot be null"); try { mService.setPerSsidRoamingMode(ssid, roamingMode, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This API allows a privileged application to remove roaming mode policy * configured using the {@link #setPerSsidRoamingMode(WifiSsid, int)}. * * Available for DO/COPE apps. * Other apps require {@code android.Manifest.permission#NETWORK_SETTINGS} or * {@code android.Manifest.permission#MANAGE_WIFI_NETWORK_SELECTION} permission. * * @param ssid SSID to be removed from the roaming mode policy. * @throws NullPointerException if the caller provided a null input. * @throws SecurityException if caller does not have the required permission. * @throws UnsupportedOperationException if the set operation is not supported on this SDK or * if the feature is not available * {@link #isAggressiveRoamingModeSupported()}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @SuppressLint("RequiresPermission") public void removePerSsidRoamingMode(@NonNull WifiSsid ssid) { Objects.requireNonNull(ssid, "ssid cannot be null"); try { mService.removePerSsidRoamingMode(ssid, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This API allows a privileged application to get roaming mode policies * configured using the {@link #setPerSsidRoamingMode(WifiSsid, int)}. * * Available for DO/COPE apps. * Other apps require {@code android.Manifest.permission#NETWORK_SETTINGS} or * {@code android.Manifest.permission#MANAGE_WIFI_NETWORK_SELECTION} permission. * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return the corresponding * roaming policies for the API caller. * @throws SecurityException if caller does not have the required permission. * @throws UnsupportedOperationException if the get operation is not supported on this SDK or * if the feature is not available * {@link #isAggressiveRoamingModeSupported()}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @SuppressLint("RequiresPermission") public void getPerSsidRoamingModes(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer> resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.getPerSsidRoamingModes(mContext.getOpPackageName(), new IMapListener.Stub() { @Override public void onResult(Map roamingPolicies) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(roamingPolicies); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Bundle key to check target wake time requester mode supported or not * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final String TWT_CAPABILITIES_KEY_BOOLEAN_TWT_REQUESTER = "key_requester"; /** * Bundle key to get minimum wake duration supported in microseconds * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final String TWT_CAPABILITIES_KEY_INT_MIN_WAKE_DURATION_MICROS = "key_min_wake_duration"; /** * Bundle key to get maximum wake duration supported in microseconds * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final String TWT_CAPABILITIES_KEY_INT_MAX_WAKE_DURATION_MICROS = "key_max_wake_duration"; /** * Bundle key to get minimum wake interval supported in microseconds * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final String TWT_CAPABILITIES_KEY_LONG_MIN_WAKE_INTERVAL_MICROS = "key_min_wake_interval"; /** * Bundle key to get maximum wake interval supported in microseconds * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final String TWT_CAPABILITIES_KEY_LONG_MAX_WAKE_INTERVAL_MICROS = "key_max_wake_interval"; /** @hide */ @StringDef(prefix = { "TWT_CAPABILITIES_KEY_"}, value = { TWT_CAPABILITIES_KEY_BOOLEAN_TWT_REQUESTER, TWT_CAPABILITIES_KEY_INT_MIN_WAKE_DURATION_MICROS, TWT_CAPABILITIES_KEY_INT_MAX_WAKE_DURATION_MICROS, TWT_CAPABILITIES_KEY_LONG_MIN_WAKE_INTERVAL_MICROS, TWT_CAPABILITIES_KEY_LONG_MAX_WAKE_INTERVAL_MICROS, }) @Retention(RetentionPolicy.SOURCE) public @interface TwtCapabilities {} /** * Get target wake time (TWT) capabilities of the primary station interface. * * Note: Target wake time feature is only supported for primary station. If Wi-Fi is off or the * capability is not available the asynchronous callback will be called with the bundle * with values { false, -1, -1, -1, -1 }. * * @param executor Executor to execute listener callback * @param resultCallback An asynchronous callback that will return a bundle for target wake time * capabilities. See {@link TwtCapabilities} for the string keys for * the bundle. * @throws SecurityException if the caller does not have permission. * @throws NullPointerException if the caller provided null inputs. * @throws UnsupportedOperationException if the API is not supported. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public void getTwtCapabilities(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultCallback, "resultCallback cannot be null"); if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); } try { Bundle extras = new Bundle(); extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); mService.getTwtCapabilities( new ITwtCapabilitiesListener.Stub() { @Override public void onResult(Bundle value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultCallback.accept(value); }); } }, extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private class TwtCallbackProxy extends ITwtCallback.Stub { private final Executor mExecutor; private final TwtSessionCallback mCallback; private TwtCallbackProxy(Executor executor, TwtSessionCallback callback) { mExecutor = executor; mCallback = callback; } @Override public void onFailure(@TwtSessionCallback.TwtErrorCode int errorCode) throws RemoteException { if (mVerboseLoggingEnabled) { Log.v(TAG, "TwtCallbackProxy: onFailure(errorCode = " + errorCode + " )"); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mCallback.onFailure(errorCode)); } @Override public void onTeardown(@TwtSessionCallback.TwtReasonCode int reasonCode) throws RemoteException { if (mVerboseLoggingEnabled) { Log.v(TAG, "TwtCallbackProxy: onTeardown(errorCode = " + reasonCode + " )"); } Binder.clearCallingIdentity(); mExecutor.execute(() -> mCallback.onTeardown(reasonCode)); } @Override public void onCreate(int wakeDuration, long wakeInterval, int mloLinkId, int owner, int sessionId) throws RemoteException { if (mVerboseLoggingEnabled) { Log.v(TAG, "TwtCallbackProxy: onCreate " + sessionId); } WifiTwtSession wifiTwtSession = new WifiTwtSession(WifiManager.this, wakeDuration, wakeInterval, mloLinkId, owner, sessionId); Binder.clearCallingIdentity(); mExecutor.execute(() -> mCallback.onCreate(wifiTwtSession)); } } /** * Set up a TWT session with a TWT responder capable AP. Only supported for primary connected * station which is a TWT requester. See {@link #getTwtCapabilities(Executor, Consumer)} and * {@link ScanResult#isTwtResponder()} to check station and AP support. * * Following callbacks are invoked, * - {@link TwtSessionCallback#onFailure(int)} upon error with error code. * - {@link TwtSessionCallback#onCreate(TwtSession)} upon TWT session creation. * - {@link TwtSessionCallback#onTeardown(int)} upon TWT session teardown. * * Note: {@link #getTwtCapabilities(Executor, Consumer)} gives {@link TwtCapabilities} which can * be used to fill in the valid TWT wake interval and duration ranges for {@link TwtRequest}. * * @param twtRequest TWT request * @param executor Executor to execute listener callback on * @param callback Callback to register * @throws SecurityException if the caller does not have permission. * @throws NullPointerException if the caller provided null inputs. * @throws UnsupportedOperationException if the API is not supported. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public void setupTwtSession(@NonNull TwtRequest twtRequest, @NonNull @CallbackExecutor Executor executor, @NonNull TwtSessionCallback callback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(callback, "callback cannot be null"); Objects.requireNonNull(twtRequest, "twtRequest cannot be null"); if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); } try { ITwtCallback.Stub binderCallback = new TwtCallbackProxy(executor, callback); Bundle extras = new Bundle(); extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); mService.setupTwtSession(twtRequest, binderCallback, extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get stats of the target wake time session. * * Note: For Internal use only. Expected to be called through * {@link TwtSession#getStats(Executor, Consumer)}. If the command fails, -1 will be returned * for all stats values. * * @param sessionId TWT session id * @param executor The executor on which callback will be invoked. * @param resultCallback The asynchronous callback that will return bundle with key string * {@link TwtSession.TwtStats}. * * @throws SecurityException if the caller does not have permission. * @throws NullPointerException if the caller provided null inputs. * @throws UnsupportedOperationException if the API is not supported or primary station is * not connected. * @hide */ public void getStatsTwtSession(@NonNull int sessionId, @NonNull Executor executor, @NonNull Consumer resultCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultCallback, "resultsCallback cannot be null"); if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); } try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.getStatsTwtSession(sessionId, new ITwtStatsListener.Stub() { @Override public void onResult(Bundle value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultCallback.accept(value); }); } }, extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Teardown the target wake time session. Only owner can teardown the session. * * Note: For internal use only. Expected to be called through * {@link TwtSessionCallback#onTeardown(int)}. * * @param sessionId TWT session id * @throws SecurityException if the caller does not have permission. * @throws UnsupportedOperationException if the API is not supported or primary station is not * connected. * @hide */ public void teardownTwtSession(int sessionId) { if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); } try { Bundle extras = new Bundle(); if (SdkLevel.isAtLeastS()) { extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mContext.getAttributionSource()); } mService.teardownTwtSession(sessionId, extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Allows a privileged application to set whether or not this device allows * device-to-device connections when infra STA is disabled. Callers can use * {@link #queryD2dAllowedWhenInfraStaDisabled(Executor, Consumer)} to check the currently * set value. * * Note: This functionality is supported only when the device support device-to-device * when infra STA is disabled. Use {@link #isD2dSupportedWhenInfraStaDisabled()} to * know if device supported device-to-device when infra STA is disabled. * * @param isAllowed whether or not the device allows to device-to-device connectivity when * infra STA is disabled. * @throws SecurityException if the caller does not have permission. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD }) @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void setD2dAllowedWhenInfraStaDisabled(boolean isAllowed) { try { mService.setD2dAllowedWhenInfraStaDisabled(isAllowed); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Query whether or not this device is configured to allow D2d connection when * infra STA is disabled. * see: {@link #setD2dAllowedWhenInfraStaDisabled(boolean)}. * * * @param executor The executor on which callback will be invoked. * @param resultsCallback An asynchronous callback that will return {@code Boolean} indicating * whether device-to-device connection is allowed or disallowed * when infra STA is disabled. * @hide */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SystemApi public void queryD2dAllowedWhenInfraStaDisabled(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); try { mService.queryD2dAllowedWhenInfraStaDisabled( new IBooleanListener.Stub() { @Override public void onResult(boolean value) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(value); }); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } }