/* * Copyright (C) 2011 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.p2p; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.net.MacAddress; import android.net.NetworkInfo; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.net.wifi.WpsInfo; import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceResponse; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceResponse; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.text.TextUtils; import android.util.CloseGuard; import android.util.Log; import android.util.SparseArray; import android.view.Display; import androidx.annotation.RequiresApi; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; 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.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * This class provides the API for managing Wi-Fi peer-to-peer connectivity. This lets an * application discover available peers, setup connection to peers and query for the list of peers. * When a p2p connection is formed over wifi, the device continues to maintain the uplink * connection over mobile or any other available network for internet connectivity on the device. * *

The API is asynchronous and responses to requests from an application are on listener * callbacks provided by the application. The application needs to do an initialization with * {@link #initialize} before doing any p2p operation. * *

Most application calls need a {@link ActionListener} instance for receiving callbacks * {@link ActionListener#onSuccess} or {@link ActionListener#onFailure}. Action callbacks * indicate whether the initiation of the action was a success or a failure. * Upon failure, the reason of failure can be one of {@link #ERROR}, {@link #P2P_UNSUPPORTED} * or {@link #BUSY}. * *

An application can initiate discovery of peers with {@link #discoverPeers}. An initiated * discovery request from an application stays active until the device starts connecting to a peer * ,forms a p2p group or there is an explicit {@link #stopPeerDiscovery}. * Applications can listen to {@link #WIFI_P2P_DISCOVERY_CHANGED_ACTION} to know if a peer-to-peer * discovery is running or stopped. Additionally, {@link #WIFI_P2P_PEERS_CHANGED_ACTION} indicates * if the peer list has changed. * *

When an application needs to fetch the current list of peers, it can request the list * of peers with {@link #requestPeers}. When the peer list is available * {@link PeerListListener#onPeersAvailable} is called with the device list. * *

An application can initiate a connection request to a peer through {@link #connect}. See * {@link WifiP2pConfig} for details on setting up the configuration. For communication with legacy * Wi-Fi devices that do not support p2p, an app can create a group using {@link #createGroup} * which creates an access point whose details can be fetched with {@link #requestGroupInfo}. * *

After a successful group formation through {@link #createGroup} or through {@link #connect}, * use {@link #requestConnectionInfo} to fetch the connection details. The connection info * {@link WifiP2pInfo} contains the address of the group owner * {@link WifiP2pInfo#groupOwnerAddress} and a flag {@link WifiP2pInfo#isGroupOwner} to indicate * if the current device is a p2p group owner. A p2p client can thus communicate with * the p2p group owner through a socket connection. If the current device is the p2p group owner, * {@link WifiP2pInfo#groupOwnerAddress} is anonymized unless the caller holds the * {@code android.Manifest.permission#LOCAL_MAC_ADDRESS} permission. * *

With peer discovery using {@link #discoverPeers}, an application discovers the neighboring * peers, but has no good way to figure out which peer to establish a connection with. For example, * if a game application is interested in finding all the neighboring peers that are also running * the same game, it has no way to find out until after the connection is setup. Pre-association * service discovery is meant to address this issue of filtering the peers based on the running * services. * *

With pre-association service discovery, an application can advertise a service for a * application on a peer device prior to a connection setup between the devices. * Currently, DNS based service discovery (Bonjour) and Upnp are the higher layer protocols * supported. Get Bonjour resources at dns-sd.org and Upnp resources at upnp.org * As an example, a video application can discover a Upnp capable media renderer * prior to setting up a Wi-fi p2p connection with the device. * *

An application can advertise a Upnp or a Bonjour service with a call to * {@link #addLocalService}. After a local service is added, * the framework automatically responds to a peer application discovering the service prior * to establishing a p2p connection. A call to {@link #removeLocalService} removes a local * service and {@link #clearLocalServices} can be used to clear all local services. * *

An application that is looking for peer devices that support certain services * can do so with a call to {@link #discoverServices}. Prior to initiating the discovery, * application can add service discovery request with a call to {@link #addServiceRequest}, * remove a service discovery request with a call to {@link #removeServiceRequest} or clear * all requests with a call to {@link #clearServiceRequests}. When no service requests remain, * a previously running service discovery will stop. * * The application is notified of a result of service discovery request through listener callbacks * set through {@link #setDnsSdResponseListeners} for Bonjour or * {@link #setUpnpServiceResponseListener} for Upnp. * *

Note: * Registering an application handler with {@link #initialize} requires the permissions * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and * {@link android.Manifest.permission#CHANGE_WIFI_STATE} to perform any further peer-to-peer * operations. * * {@see WifiP2pConfig} * {@see WifiP2pInfo} * {@see WifiP2pGroup} * {@see WifiP2pDevice} * {@see WifiP2pDeviceList} * {@see android.net.wifi.WpsInfo} */ @SystemService(Context.WIFI_P2P_SERVICE) public class WifiP2pManager { private static final String TAG = "WifiP2pManager"; /** @hide */ public static final long FEATURE_SET_VENDOR_ELEMENTS = 1L << 0; /** @hide */ public static final long FEATURE_FLEXIBLE_DISCOVERY = 1L << 1; /** @hide */ public static final long FEATURE_GROUP_CLIENT_REMOVAL = 1L << 2; /** @hide */ public static final long FEATURE_GROUP_OWNER_IPV6_LINK_LOCAL_ADDRESS_PROVIDED = 1L << 3; /** * Extra for transporting a WifiP2pConfig * @hide */ public static final String EXTRA_PARAM_KEY_CONFIG = "android.net.wifi.p2p.EXTRA_PARAM_KEY_CONFIG"; /** * Extra for transporting a WifiP2pServiceInfo * @hide */ public static final String EXTRA_PARAM_KEY_SERVICE_INFO = "android.net.wifi.p2p.EXTRA_PARAM_KEY_SERVICE_INFO"; /** * Extra for transporting a peer discovery frequency. * @hide */ public static final String EXTRA_PARAM_KEY_PEER_DISCOVERY_FREQ = "android.net.wifi.p2p.EXTRA_PARAM_KEY_PEER_DISCOVERY_FREQ"; /** * Extra for transporting a peer MAC address. * @hide */ public static final String EXTRA_PARAM_KEY_PEER_ADDRESS = "android.net.wifi.p2p.EXTRA_PARAM_KEY_PEER_ADDRESS"; /** * Extra used to indicate that a message is sent from Wifi internally * @hide */ public static final String EXTRA_PARAM_KEY_INTERNAL_MESSAGE = "android.net.wifi.p2p.EXTRA_PARAM_KEY_INTERNAL_MESSAGE"; /** * Used to communicate the Display ID for multi display devices. * @hide **/ public static final String EXTRA_PARAM_KEY_DISPLAY_ID = "android.net.wifi.p2p.EXTRA_PARAM_KEY_DISPLAY_ID"; /** * Extra for transporting a WifiP2pDevice. * @hide */ public static final String EXTRA_PARAM_KEY_DEVICE = "android.net.wifi.p2p.EXTRA_PARAM_KEY_DEVICE"; /** * Extra for transporting a WPS PIN. * @hide */ public static final String EXTRA_PARAM_KEY_WPS_PIN = "android.net.wifi.p2p.EXTRA_PARAM_KEY_WPS_PIN"; /** * Extra for transporting vendor-specific information element list * @hide */ public static final String EXTRA_PARAM_KEY_INFORMATION_ELEMENT_LIST = "android.net.wifi.p2p.EXTRA_PARAM_KEY_INFORMATION_ELEMENT_LIST"; /** * Extra for transporting discovery config with vendor-specific data * @hide */ public static final String EXTRA_PARAM_KEY_DISCOVERY_CONFIG = "android.net.wifi.p2p.EXTRA_PARAM_KEY_DISCOVERY_CONFIG"; /** * Extra for transporting extended listening parameters * @hide */ public static final String EXTRA_PARAM_KEY_EXT_LISTEN_PARAMS = "android.net.wifi.p2p.EXTRA_PARAM_KEY_EXT_LISTEN_PARAMS"; /** * Key for transporting a bundle of extra information. * @hide */ public static final String EXTRA_PARAM_KEY_BUNDLE = "android.net.wifi.p2p.EXTRA_PARAM_KEY_BUNDLE"; /** * Broadcast intent action to indicate whether Wi-Fi p2p is enabled or disabled. An * extra {@link #EXTRA_WIFI_STATE} provides the state information as int. * * @see #EXTRA_WIFI_STATE */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WIFI_P2P_STATE_CHANGED_ACTION = "android.net.wifi.p2p.STATE_CHANGED"; /** * The lookup key for an int that indicates whether Wi-Fi p2p is enabled or disabled. * Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. * * @see #WIFI_P2P_STATE_DISABLED * @see #WIFI_P2P_STATE_ENABLED */ public static final String EXTRA_WIFI_STATE = "wifi_p2p_state"; /** @hide */ @IntDef({ WIFI_P2P_STATE_DISABLED, WIFI_P2P_STATE_ENABLED}) @Retention(RetentionPolicy.SOURCE) public @interface WifiP2pState { } /** * Wi-Fi p2p is disabled. * * @see #WIFI_P2P_STATE_CHANGED_ACTION */ public static final int WIFI_P2P_STATE_DISABLED = 1; /** * Wi-Fi p2p is enabled. * * @see #WIFI_P2P_STATE_CHANGED_ACTION */ public static final int WIFI_P2P_STATE_ENABLED = 2; /** * Broadcast intent action indicating that the state of Wi-Fi p2p connectivity * has changed. One extra {@link #EXTRA_WIFI_P2P_INFO} provides the p2p connection info in * the form of a {@link WifiP2pInfo} object. Another extra {@link #EXTRA_NETWORK_INFO} provides * the network info in the form of a {@link android.net.NetworkInfo}. A third extra provides * the details of the group and may contain a {@code null}. * * All of these permissions are required to receive this broadcast: * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and either * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or * {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} * * @see #EXTRA_WIFI_P2P_INFO * @see #EXTRA_NETWORK_INFO * @see #EXTRA_WIFI_P2P_GROUP */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WIFI_P2P_CONNECTION_CHANGED_ACTION = "android.net.wifi.p2p.CONNECTION_STATE_CHANGE"; /** * The lookup key for a {@link android.net.wifi.p2p.WifiP2pInfo} object * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. */ public static final String EXTRA_WIFI_P2P_INFO = "wifiP2pInfo"; /** * The lookup key for a {@link android.net.NetworkInfo} object associated with the * p2p network. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. */ public static final String EXTRA_NETWORK_INFO = "networkInfo"; /** * The lookup key for a {@link android.net.wifi.p2p.WifiP2pGroup} object * associated with the p2p network. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. */ public static final String EXTRA_WIFI_P2P_GROUP = "p2pGroupInfo"; /** * Broadcast intent action indicating that the available peer list has changed. This * can be sent as a result of peers being found, lost or updated. * * All of these permissions are required to receive this broadcast: * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and either * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or * {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} * *

An extra {@link #EXTRA_P2P_DEVICE_LIST} provides the full list of * current peers. The full list of peers can also be obtained any time with * {@link #requestPeers}. * * @see #EXTRA_P2P_DEVICE_LIST */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WIFI_P2P_PEERS_CHANGED_ACTION = "android.net.wifi.p2p.PEERS_CHANGED"; /** * The lookup key for a {@link android.net.wifi.p2p.WifiP2pDeviceList} object representing * the new peer list when {@link #WIFI_P2P_PEERS_CHANGED_ACTION} broadcast is sent. * *

Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. */ public static final String EXTRA_P2P_DEVICE_LIST = "wifiP2pDeviceList"; /** * Broadcast intent action indicating that peer discovery has either started or stopped. * One extra {@link #EXTRA_DISCOVERY_STATE} indicates whether discovery has started * or stopped. * *

Note that discovery will be stopped during a connection setup. If the application tries * to re-initiate discovery during this time, it can fail. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WIFI_P2P_DISCOVERY_CHANGED_ACTION = "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE"; /** * The lookup key for an int that indicates whether p2p discovery has started or stopped. * Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. * * @see #WIFI_P2P_DISCOVERY_STARTED * @see #WIFI_P2P_DISCOVERY_STOPPED */ public static final String EXTRA_DISCOVERY_STATE = "discoveryState"; /** @hide */ @IntDef({ WIFI_P2P_DISCOVERY_STOPPED, WIFI_P2P_DISCOVERY_STARTED}) @Retention(RetentionPolicy.SOURCE) public @interface WifiP2pDiscoveryState { } /** * p2p discovery has stopped * * @see #WIFI_P2P_DISCOVERY_CHANGED_ACTION */ public static final int WIFI_P2P_DISCOVERY_STOPPED = 1; /** * p2p discovery has started * * @see #WIFI_P2P_DISCOVERY_CHANGED_ACTION */ public static final int WIFI_P2P_DISCOVERY_STARTED = 2; /** * Broadcast intent action indicating that peer listen has either started or stopped. * One extra {@link #EXTRA_LISTEN_STATE} indicates whether listen has started or stopped. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_WIFI_P2P_LISTEN_STATE_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_LISTEN_STATE_CHANGED"; /** * The lookup key for an int that indicates whether p2p listen has started or stopped. * Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. * * @see #WIFI_P2P_LISTEN_STARTED * @see #WIFI_P2P_LISTEN_STOPPED */ public static final String EXTRA_LISTEN_STATE = "android.net.wifi.p2p.extra.LISTEN_STATE"; /** @hide */ @IntDef({ WIFI_P2P_LISTEN_STOPPED, WIFI_P2P_LISTEN_STARTED}) @Retention(RetentionPolicy.SOURCE) public @interface WifiP2pListenState { } /** * p2p listen has stopped * * @see #ACTION_WIFI_P2P_LISTEN_STATE_CHANGED */ public static final int WIFI_P2P_LISTEN_STOPPED = 1; /** * p2p listen has started * * @see #ACTION_WIFI_P2P_LISTEN_STATE_CHANGED */ public static final int WIFI_P2P_LISTEN_STARTED = 2; /** * Broadcast intent action indicating that this device details have changed. * *

An extra {@link #EXTRA_WIFI_P2P_DEVICE} provides this device details. * The valid device details can also be obtained with * {@link #requestDeviceInfo(Channel, DeviceInfoListener)} when p2p is enabled. * To get information notifications on P2P getting enabled refers * {@link #WIFI_P2P_STATE_ENABLED}. * *

The {@link #EXTRA_WIFI_P2P_DEVICE} extra contains an anonymized version of the device's * MAC address. Callers holding the {@code android.Manifest.permission#LOCAL_MAC_ADDRESS} * permission can use {@link #requestDeviceInfo} to obtain the actual MAC address of this * device. * * All of these permissions are required to receive this broadcast: * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and either * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or * {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} * * @see #EXTRA_WIFI_P2P_DEVICE */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WIFI_P2P_THIS_DEVICE_CHANGED_ACTION = "android.net.wifi.p2p.THIS_DEVICE_CHANGED"; /** * The lookup key for a {@link android.net.wifi.p2p.WifiP2pDevice} object * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. */ public static final String EXTRA_WIFI_P2P_DEVICE = "wifiP2pDevice"; /** * Broadcast intent action indicating that remembered persistent groups have changed. * * You can not receive this through components declared * in manifests, only by explicitly registering for it with * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, * android.content.IntentFilter) Context.registerReceiver()}. * * @hide */ @SystemApi public static final String ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED"; /** * Broadcast intent action indicating whether or not current connecting * request is accepted. * * The connecting request is initiated by * {@link #connect(Channel, WifiP2pConfig, ActionListener)}. *

The {@link #EXTRA_REQUEST_RESPONSE} extra indicates whether or not current * request is accepted or rejected. *

The {@link #EXTRA_REQUEST_CONFIG} extra indicates the responsed configuration. */ public static final String ACTION_WIFI_P2P_REQUEST_RESPONSE_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_REQUEST_RESPONSE_CHANGED"; /** * The lookup key for the result of a request, true if accepted, false otherwise. */ public static final String EXTRA_REQUEST_RESPONSE = "android.net.wifi.p2p.extra.REQUEST_RESPONSE"; /** * The lookup key for the {@link WifiP2pConfig} object of a request. */ public static final String EXTRA_REQUEST_CONFIG = "android.net.wifi.p2p.extra.REQUEST_CONFIG"; /** * The lookup key for a handover message returned by the WifiP2pService. * @hide */ public static final String EXTRA_HANDOVER_MESSAGE = "android.net.wifi.p2p.EXTRA_HANDOVER_MESSAGE"; /** * The lookup key for a calling package name from WifiP2pManager * @hide */ public static final String CALLING_PACKAGE = "android.net.wifi.p2p.CALLING_PACKAGE"; /** * The lookup key for a calling feature id from WifiP2pManager * @hide */ public static final String CALLING_FEATURE_ID = "android.net.wifi.p2p.CALLING_FEATURE_ID"; /** * The lookup key for a calling package binder from WifiP2pManager * @hide */ public static final String CALLING_BINDER = "android.net.wifi.p2p.CALLING_BINDER"; /** * Run P2P scan on all channels. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int WIFI_P2P_SCAN_FULL = 0; /** * Run P2P scan only on social channels. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int WIFI_P2P_SCAN_SOCIAL = 1; /** * Run P2P scan only on a specific channel. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int WIFI_P2P_SCAN_SINGLE_FREQ = 2; /** * Run P2P scan with config Params. * @hide */ public static final int WIFI_P2P_SCAN_WITH_CONFIG_PARAMS = 3; /** @hide */ @IntDef(prefix = {"WIFI_P2P_SCAN_"}, value = { WIFI_P2P_SCAN_FULL, WIFI_P2P_SCAN_SOCIAL, WIFI_P2P_SCAN_SINGLE_FREQ}) @Retention(RetentionPolicy.SOURCE) public @interface WifiP2pScanType { } /** * Enter the P2P listen state with additional parameters. * @hide */ public static final int WIFI_P2P_EXT_LISTEN_WITH_PARAMS = 1; /** * No channel specified for discover Peers APIs. Let lower layer decide the frequencies to scan * based on the WifiP2pScanType. * @hide */ public static final int WIFI_P2P_SCAN_FREQ_UNSPECIFIED = 0; /** * Maximum length in bytes of all vendor specific information elements (IEs) allowed to * set during Wi-Fi Direct (P2P) discovery. */ private static final int WIFI_P2P_VENDOR_ELEMENTS_MAXIMUM_LENGTH = 512; private Context mContext; IWifiP2pManager mService; private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER; /** @hide */ public static final int DISCOVER_PEERS = BASE + 1; /** @hide */ public static final int DISCOVER_PEERS_FAILED = BASE + 2; /** @hide */ public static final int DISCOVER_PEERS_SUCCEEDED = BASE + 3; /** @hide */ public static final int STOP_DISCOVERY = BASE + 4; /** @hide */ public static final int STOP_DISCOVERY_FAILED = BASE + 5; /** @hide */ public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 6; /** @hide */ public static final int CONNECT = BASE + 7; /** @hide */ public static final int CONNECT_FAILED = BASE + 8; /** @hide */ public static final int CONNECT_SUCCEEDED = BASE + 9; /** @hide */ public static final int CANCEL_CONNECT = BASE + 10; /** @hide */ public static final int CANCEL_CONNECT_FAILED = BASE + 11; /** @hide */ public static final int CANCEL_CONNECT_SUCCEEDED = BASE + 12; /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int CREATE_GROUP = BASE + 13; /** @hide */ public static final int CREATE_GROUP_FAILED = BASE + 14; /** @hide */ public static final int CREATE_GROUP_SUCCEEDED = BASE + 15; /** @hide */ public static final int REMOVE_GROUP = BASE + 16; /** @hide */ public static final int REMOVE_GROUP_FAILED = BASE + 17; /** @hide */ public static final int REMOVE_GROUP_SUCCEEDED = BASE + 18; /** @hide */ public static final int REQUEST_PEERS = BASE + 19; /** @hide */ public static final int RESPONSE_PEERS = BASE + 20; /** @hide */ public static final int REQUEST_CONNECTION_INFO = BASE + 21; /** @hide */ public static final int RESPONSE_CONNECTION_INFO = BASE + 22; /** @hide */ public static final int REQUEST_GROUP_INFO = BASE + 23; /** @hide */ public static final int RESPONSE_GROUP_INFO = BASE + 24; /** @hide */ public static final int ADD_LOCAL_SERVICE = BASE + 28; /** @hide */ public static final int ADD_LOCAL_SERVICE_FAILED = BASE + 29; /** @hide */ public static final int ADD_LOCAL_SERVICE_SUCCEEDED = BASE + 30; /** @hide */ public static final int REMOVE_LOCAL_SERVICE = BASE + 31; /** @hide */ public static final int REMOVE_LOCAL_SERVICE_FAILED = BASE + 32; /** @hide */ public static final int REMOVE_LOCAL_SERVICE_SUCCEEDED = BASE + 33; /** @hide */ public static final int CLEAR_LOCAL_SERVICES = BASE + 34; /** @hide */ public static final int CLEAR_LOCAL_SERVICES_FAILED = BASE + 35; /** @hide */ public static final int CLEAR_LOCAL_SERVICES_SUCCEEDED = BASE + 36; /** @hide */ public static final int ADD_SERVICE_REQUEST = BASE + 37; /** @hide */ public static final int ADD_SERVICE_REQUEST_FAILED = BASE + 38; /** @hide */ public static final int ADD_SERVICE_REQUEST_SUCCEEDED = BASE + 39; /** @hide */ public static final int REMOVE_SERVICE_REQUEST = BASE + 40; /** @hide */ public static final int REMOVE_SERVICE_REQUEST_FAILED = BASE + 41; /** @hide */ public static final int REMOVE_SERVICE_REQUEST_SUCCEEDED = BASE + 42; /** @hide */ public static final int CLEAR_SERVICE_REQUESTS = BASE + 43; /** @hide */ public static final int CLEAR_SERVICE_REQUESTS_FAILED = BASE + 44; /** @hide */ public static final int CLEAR_SERVICE_REQUESTS_SUCCEEDED = BASE + 45; /** @hide */ public static final int DISCOVER_SERVICES = BASE + 46; /** @hide */ public static final int DISCOVER_SERVICES_FAILED = BASE + 47; /** @hide */ public static final int DISCOVER_SERVICES_SUCCEEDED = BASE + 48; /** @hide */ public static final int PING = BASE + 49; /** @hide */ public static final int RESPONSE_SERVICE = BASE + 50; /** @hide */ public static final int SET_DEVICE_NAME = BASE + 51; /** @hide */ public static final int SET_DEVICE_NAME_FAILED = BASE + 52; /** @hide */ public static final int SET_DEVICE_NAME_SUCCEEDED = BASE + 53; /** @hide */ public static final int DELETE_PERSISTENT_GROUP = BASE + 54; /** @hide */ public static final int DELETE_PERSISTENT_GROUP_FAILED = BASE + 55; /** @hide */ public static final int DELETE_PERSISTENT_GROUP_SUCCEEDED = BASE + 56; /** @hide */ public static final int REQUEST_PERSISTENT_GROUP_INFO = BASE + 57; /** @hide */ public static final int RESPONSE_PERSISTENT_GROUP_INFO = BASE + 58; /** @hide */ public static final int SET_WFD_INFO = BASE + 59; /** @hide */ public static final int SET_WFD_INFO_FAILED = BASE + 60; /** @hide */ public static final int SET_WFD_INFO_SUCCEEDED = BASE + 61; /** @hide */ public static final int START_WPS = BASE + 62; /** @hide */ public static final int START_WPS_FAILED = BASE + 63; /** @hide */ public static final int START_WPS_SUCCEEDED = BASE + 64; /** @hide */ public static final int START_LISTEN = BASE + 65; /** @hide */ public static final int START_LISTEN_FAILED = BASE + 66; /** @hide */ public static final int START_LISTEN_SUCCEEDED = BASE + 67; /** @hide */ public static final int STOP_LISTEN = BASE + 68; /** @hide */ public static final int STOP_LISTEN_FAILED = BASE + 69; /** @hide */ public static final int STOP_LISTEN_SUCCEEDED = BASE + 70; /** @hide */ public static final int SET_CHANNEL = BASE + 71; /** @hide */ public static final int SET_CHANNEL_FAILED = BASE + 72; /** @hide */ public static final int SET_CHANNEL_SUCCEEDED = BASE + 73; /** @hide */ public static final int GET_HANDOVER_REQUEST = BASE + 75; /** @hide */ public static final int GET_HANDOVER_SELECT = BASE + 76; /** @hide */ public static final int RESPONSE_GET_HANDOVER_MESSAGE = BASE + 77; /** @hide */ public static final int INITIATOR_REPORT_NFC_HANDOVER = BASE + 78; /** @hide */ public static final int RESPONDER_REPORT_NFC_HANDOVER = BASE + 79; /** @hide */ public static final int REPORT_NFC_HANDOVER_SUCCEEDED = BASE + 80; /** @hide */ public static final int REPORT_NFC_HANDOVER_FAILED = BASE + 81; /** @hide */ public static final int FACTORY_RESET = BASE + 82; /** @hide */ public static final int FACTORY_RESET_FAILED = BASE + 83; /** @hide */ public static final int FACTORY_RESET_SUCCEEDED = BASE + 84; /** @hide */ public static final int REQUEST_ONGOING_PEER_CONFIG = BASE + 85; /** @hide */ public static final int RESPONSE_ONGOING_PEER_CONFIG = BASE + 86; /** @hide */ public static final int SET_ONGOING_PEER_CONFIG = BASE + 87; /** @hide */ public static final int SET_ONGOING_PEER_CONFIG_FAILED = BASE + 88; /** @hide */ public static final int SET_ONGOING_PEER_CONFIG_SUCCEEDED = BASE + 89; /** @hide */ public static final int REQUEST_P2P_STATE = BASE + 90; /** @hide */ public static final int RESPONSE_P2P_STATE = BASE + 91; /** @hide */ public static final int REQUEST_DISCOVERY_STATE = BASE + 92; /** @hide */ public static final int RESPONSE_DISCOVERY_STATE = BASE + 93; /** @hide */ public static final int REQUEST_NETWORK_INFO = BASE + 94; /** @hide */ public static final int RESPONSE_NETWORK_INFO = BASE + 95; /** @hide */ public static final int UPDATE_CHANNEL_INFO = BASE + 96; /** @hide */ public static final int REQUEST_DEVICE_INFO = BASE + 97; /** @hide */ public static final int RESPONSE_DEVICE_INFO = BASE + 98; /** @hide */ public static final int REMOVE_CLIENT = BASE + 99; /** @hide */ public static final int REMOVE_CLIENT_FAILED = BASE + 100; /** @hide */ public static final int REMOVE_CLIENT_SUCCEEDED = BASE + 101; /** @hide */ public static final int ADD_EXTERNAL_APPROVER = BASE + 102; /** @hide */ public static final int EXTERNAL_APPROVER_ATTACH = BASE + 103; /** @hide */ public static final int EXTERNAL_APPROVER_DETACH = BASE + 104; /** @hide */ public static final int EXTERNAL_APPROVER_CONNECTION_REQUESTED = BASE + 105; /** @hide */ public static final int EXTERNAL_APPROVER_PIN_GENERATED = BASE + 106; /** @hide */ public static final int REMOVE_EXTERNAL_APPROVER = BASE + 107; /** @hide */ public static final int REMOVE_EXTERNAL_APPROVER_FAILED = BASE + 108; /** @hide */ public static final int REMOVE_EXTERNAL_APPROVER_SUCCEEDED = BASE + 109; /** @hide */ public static final int SET_CONNECTION_REQUEST_RESULT = BASE + 110; /** @hide */ public static final int SET_CONNECTION_REQUEST_RESULT_FAILED = BASE + 111; /** @hide */ public static final int SET_CONNECTION_REQUEST_RESULT_SUCCEEDED = BASE + 112; /** @hide */ public static final int SET_VENDOR_ELEMENTS = BASE + 113; /** @hide */ public static final int SET_VENDOR_ELEMENTS_FAILED = BASE + 114; /** @hide */ public static final int SET_VENDOR_ELEMENTS_SUCCEEDED = BASE + 115; /** @hide */ public static final int GET_LISTEN_STATE = BASE + 116; /** @hide */ public static final int GET_LISTEN_STATE_FAILED = BASE + 117; /** @hide */ public static final int RESPONSE_GET_LISTEN_STATE = BASE + 118; private static final SparseArray sWifiP2pListenerMap = new SparseArray<>(); /** * Create a new WifiP2pManager instance. Applications use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve * the standard {@link android.content.Context#WIFI_P2P_SERVICE Context.WIFI_P2P_SERVICE}. * @param service the Binder interface * @hide - hide this because it takes in a parameter of type IWifiP2pManager, which * is a system private class. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public WifiP2pManager(IWifiP2pManager service) { mService = service; } /** * Passed with {@link ActionListener#onFailure}. * Indicates that the operation failed due to an internal error. */ public static final int ERROR = 0; /** * Passed with {@link ActionListener#onFailure}. * Indicates that the operation failed because p2p is unsupported on the device. */ public static final int P2P_UNSUPPORTED = 1; /** * Passed with {@link ActionListener#onFailure}. * Indicates that the operation failed because the framework is busy and * unable to service the request */ public static final int BUSY = 2; /** * Passed with {@link ActionListener#onFailure}. * Indicates that the {@link #discoverServices} failed because no service * requests are added. Use {@link #addServiceRequest} to add a service * request. */ public static final int NO_SERVICE_REQUESTS = 3; /** Interface for callback invocation when framework channel is lost */ public interface ChannelListener { /** * The channel to the framework has been disconnected. * Application could try re-initializing using {@link #initialize} */ public void onChannelDisconnected(); } /** Interface for callback invocation on an application action */ public interface ActionListener { /** The operation succeeded */ public void onSuccess(); /** * The operation failed * @param reason The reason for failure could be one of {@link #P2P_UNSUPPORTED}, * {@link #ERROR} or {@link #BUSY} */ public void onFailure(int reason); } /** Interface for callback invocation when peer list is available */ public interface PeerListListener { /** * The requested peer list is available * @param peers List of available peers */ public void onPeersAvailable(WifiP2pDeviceList peers); } /** Interface for callback invocation when connection info is available */ public interface ConnectionInfoListener { /** * The requested connection info is available * @param info Wi-Fi p2p connection info */ public void onConnectionInfoAvailable(WifiP2pInfo info); } /** Interface for callback invocation when group info is available */ public interface GroupInfoListener { /** * The requested p2p group info is available * @param group Wi-Fi p2p group info */ public void onGroupInfoAvailable(WifiP2pGroup group); } /** * Interface for callback invocation when service discovery response other than * Upnp or Bonjour is received */ public interface ServiceResponseListener { /** * The requested service response is available. * * @param protocolType protocol type. currently only * {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}. * @param responseData service discovery response data based on the requested * service protocol type. The format depends on the service type. * @param srcDevice source device. */ public void onServiceAvailable(int protocolType, byte[] responseData, WifiP2pDevice srcDevice); } /** * Interface for callback invocation when Bonjour service discovery response * is received */ public interface DnsSdServiceResponseListener { /** * The requested Bonjour service response is available. * *

This function is invoked when the device with the specified Bonjour * registration type returned the instance name. * @param instanceName instance name.
* e.g) "MyPrinter". * @param registrationType
* e.g) "_ipp._tcp.local." * @param srcDevice source device. */ public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice srcDevice); } /** * Interface for callback invocation when Bonjour TXT record is available * for a service */ public interface DnsSdTxtRecordListener { /** * The requested Bonjour service response is available. * *

This function is invoked when the device with the specified full * service domain service returned TXT record. * * @param fullDomainName full domain name.
* e.g) "MyPrinter._ipp._tcp.local.". * @param txtRecordMap TXT record data as a map of key/value pairs * @param srcDevice source device. */ public void onDnsSdTxtRecordAvailable(String fullDomainName, Map txtRecordMap, WifiP2pDevice srcDevice); } /** * Interface for callback invocation when upnp service discovery response * is received * */ public interface UpnpServiceResponseListener { /** * The requested upnp service response is available. * *

This function is invoked when the specified device or service is found. * * @param uniqueServiceNames The list of unique service names.
* e.g) uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device: * MediaServer:1 * @param srcDevice source device. */ public void onUpnpServiceAvailable(List uniqueServiceNames, WifiP2pDevice srcDevice); } /** * Interface for callback invocation when stored group info list is available * * @hide */ @SystemApi public interface PersistentGroupInfoListener { /** * The requested stored p2p group info list is available * @param groups Wi-Fi p2p group info list */ void onPersistentGroupInfoAvailable(@NonNull WifiP2pGroupList groups); } /** * Interface for callback invocation when Handover Request or Select Message is available * @hide */ public interface HandoverMessageListener { public void onHandoverMessageAvailable(String handoverMessage); } /** Interface for callback invocation when p2p state is available * in response to {@link #requestP2pState}. */ public interface P2pStateListener { /** * The requested p2p state is available. * @param state Wi-Fi p2p state * @see #WIFI_P2P_STATE_DISABLED * @see #WIFI_P2P_STATE_ENABLED */ void onP2pStateAvailable(@WifiP2pState int state); } /** Interface for callback invocation when p2p state is available * in response to {@link #requestDiscoveryState}. */ public interface DiscoveryStateListener { /** * The requested p2p discovery state is available. * @param state Wi-Fi p2p discovery state * @see #WIFI_P2P_DISCOVERY_STARTED * @see #WIFI_P2P_DISCOVERY_STOPPED */ void onDiscoveryStateAvailable(@WifiP2pDiscoveryState int state); } /** Interface for callback invocation when p2p state is available * in response to {@link #getListenState}. * @hide */ public interface ListenStateListener { /** * The requested p2p listen state is available. * @param state Wi-Fi p2p listen state * @see #WIFI_P2P_LISTEN_STARTED * @see #WIFI_P2P_LISTEN_STOPPED */ void onListenStateAvailable(@WifiP2pListenState int state); } /** Interface for callback invocation when {@link android.net.NetworkInfo} is available * in response to {@link #requestNetworkInfo}. */ public interface NetworkInfoListener { /** * The requested {@link android.net.NetworkInfo} is available * @param networkInfo Wi-Fi p2p {@link android.net.NetworkInfo} */ void onNetworkInfoAvailable(@NonNull NetworkInfo networkInfo); } /** * Interface for callback invocation when ongoing peer info is available * @hide */ public interface OngoingPeerInfoListener { /** * The requested ongoing WifiP2pConfig is available * @param peerConfig WifiP2pConfig for current connecting session */ void onOngoingPeerAvailable(WifiP2pConfig peerConfig); } /** Interface for callback invocation when {@link android.net.wifi.p2p.WifiP2pDevice} * is available in response to {@link #requestDeviceInfo(Channel, DeviceInfoListener)}. */ public interface DeviceInfoListener { /** * The requested {@link android.net.wifi.p2p.WifiP2pDevice} is available. * @param wifiP2pDevice Wi-Fi p2p {@link android.net.wifi.p2p.WifiP2pDevice} */ void onDeviceInfoAvailable(@Nullable WifiP2pDevice wifiP2pDevice); } /** * Interface for callback invocation when an incoming request is received. * * This callback is registered by * {@link #addExternalApprover(Channel, MacAddress, ExternalApproverRequestListener)}. */ public interface ExternalApproverRequestListener { /** * This device received a negotiation request from another peer. * * Used in {@link #onConnectionRequested(int, WifiP2pConfig, WifiP2pDevice)}. */ int REQUEST_TYPE_NEGOTIATION = 0; /** * This device received an invitation request from GO to join the group. * * Used in {@link #onConnectionRequested(int, WifiP2pConfig, WifiP2pDevice)}. */ int REQUEST_TYPE_INVITATION = 1; /** * This GO device received a request from a peer to join the group. * * Used in {@link #onConnectionRequested(int, WifiP2pConfig, WifiP2pDevice)}. */ int REQUEST_TYPE_JOIN = 2; /** @hide */ @IntDef(prefix = {"REQUEST_TYPE__"}, value = { REQUEST_TYPE_NEGOTIATION, REQUEST_TYPE_INVITATION, REQUEST_TYPE_JOIN}) @Retention(RetentionPolicy.SOURCE) public @interface RequestType { } /** * Detached by a call to * {@link #removeExternalApprover(Channel, MacAddress, ActionListener)}. * * Used in {@link #onDetached(MacAddress, int)}. */ int APPROVER_DETACH_REASON_REMOVE = 0; /** * Detached due to a framework failure. * * Used in {@link #onDetached(MacAddress, int)}. */ int APPROVER_DETACH_REASON_FAILURE = 1; /** * Detached when a new approver replaces an old one. * * Used in {@link #onDetached(MacAddress, int)}. */ int APPROVER_DETACH_REASON_REPLACE = 2; /** * Detached since the {@link WifiP2pManager} channel was closed, e.g. * by using {@link Channel#close()} method. * * Used in {@link #onDetached(MacAddress, int)}. */ int APPROVER_DETACH_REASON_CLOSE = 3; /** @hide */ @IntDef(prefix = {"APPROVER_DETACH_REASON_"}, value = { APPROVER_DETACH_REASON_REMOVE, APPROVER_DETACH_REASON_FAILURE, APPROVER_DETACH_REASON_REPLACE, APPROVER_DETACH_REASON_CLOSE}) @Retention(RetentionPolicy.SOURCE) public @interface ApproverDetachReason { } /** * Called when an approver registration via * {@link #addExternalApprover(Channel, MacAddress, ExternalApproverRequestListener)} * is successful. * * @param deviceAddress is the peer MAC address used in the registration. */ void onAttached(@NonNull MacAddress deviceAddress); /** * Called when an approver registration via * {@link #addExternalApprover(Channel, MacAddress, ExternalApproverRequestListener)} * has failed. * * @param deviceAddress is the peer MAC address used in the registration. * @param reason is the failure reason. */ void onDetached(@NonNull MacAddress deviceAddress, @ApproverDetachReason int reason); /** * Called when there is an incoming connection request * which matches a peer (identified by its {@link MacAddress}) registered by the external * approver through * {@link #addExternalApprover(Channel, MacAddress, ExternalApproverRequestListener)}. * The external approver is expected to follow up with a connection decision using the * {@link #setConnectionRequestResult(Channel, MacAddress, int, ActionListener)} with * {@link #CONNECTION_REQUEST_ACCEPT}, {@link #CONNECTION_REQUEST_REJECT}, or * {@link #CONNECTION_REQUEST_DEFER_TO_SERVICE}. * * @param requestType is one of {@link #REQUEST_TYPE_NEGOTIATION}, * {@link #REQUEST_TYPE_INVITATION}, and {@link #REQUEST_TYPE_JOIN}. * @param config is the peer configuration. * @param device is the peer information. */ void onConnectionRequested( @RequestType int requestType, @NonNull WifiP2pConfig config, @NonNull WifiP2pDevice device); /** * Called when a PIN is generated by the WiFi service. * * The external approver can display the PIN, exchange the PIN via Out-Of-Band way * or ask the wifi service to show the PIN as usual using the * {@link #setConnectionRequestResult(Channel, MacAddress, int, ActionListener)} * with {@link #CONNECTION_REQUEST_DEFER_SHOW_PIN_TO_SERVICE}. * * @param deviceAddress is the peer MAC address used in the registration. * @param pin is the WPS PIN. */ void onPinGenerated(@NonNull MacAddress deviceAddress, @NonNull String pin); } /** * Interface used to listen to Wi-Fi p2p various changes such as device state change, * discovery started/stopped, connection change, etc. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public interface WifiP2pListener { /** * Called when Wi-Fi p2p has been enabled or disabled. * @see #WIFI_P2P_STATE_CHANGED_ACTION * @see #requestP2pState(Channel, P2pStateListener) * * @param state indicates whether Wi-Fi p2p is enabled or disabled. * @see #WIFI_P2P_STATE_ENABLED * @see #WIFI_P2P_STATE_DISABLED */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onP2pStateChanged(@WifiP2pState int state) { } /** * Called when peer discovery has either started or stopped. * @see #WIFI_P2P_DISCOVERY_CHANGED_ACTION * @see #requestDiscoveryState(Channel, DiscoveryStateListener) * * @param state indicates whether discovery has started or stopped. * @see #WIFI_P2P_DISCOVERY_STARTED * @see #WIFI_P2P_DISCOVERY_STOPPED */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onDiscoveryStateChanged(@WifiP2pDiscoveryState int state) { } /** * Called when peer listen has either started or stopped. * @see #ACTION_WIFI_P2P_LISTEN_STATE_CHANGED * @see #getListenState(Channel, Executor, Consumer) * * @param state indicates whether listen has started or stopped. * @see #WIFI_P2P_LISTEN_STARTED * @see #WIFI_P2P_LISTEN_STOPPED */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onListenStateChanged(@WifiP2pListenState int state) { } /** * Called when this device details have changed. * @see #WIFI_P2P_THIS_DEVICE_CHANGED_ACTION * @see #requestDeviceInfo(Channel, DeviceInfoListener) * * @param p2pDevice provides this device details. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onDeviceConfigurationChanged(@Nullable WifiP2pDevice p2pDevice) { } /** * Called when the available peer list has changed. This can be sent as a result of peers * being found, lost or updated. * @see #WIFI_P2P_PEERS_CHANGED_ACTION * @see #requestPeers(Channel, PeerListListener) * * @param p2pDeviceList provides the full list of current peers. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onPeerListChanged(@NonNull WifiP2pDeviceList p2pDeviceList) { } /** * Called when remembered persistent groups have changed. * @see #ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED * @see #requestPersistentGroupInfo(Channel, PersistentGroupInfoListener) * * @param p2pGroupList provides the full list of p2p group. * * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onPersistentGroupsChanged(@NonNull WifiP2pGroupList p2pGroupList) { } /** * Called when either group owner or group client is creating p2p group. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onGroupCreating() { } /** * Called when group negotiation has been rejected by user. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onGroupNegotiationRejectedByUser() { } /** * Called when group creation has failed. * * @param reason provides the group creation failure reason. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onGroupCreationFailed(@GroupCreationFailureReason int reason) { } /** * Called when either group owner or group client has created p2p group successfully. * * @param p2pInfo provides the p2p connection info. * @param p2pGroup provides the details of the group. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onGroupCreated(@NonNull WifiP2pInfo p2pInfo, @NonNull WifiP2pGroup p2pGroup) { } /** * Called to indicate group owner that a group client has joined p2p group successfully. * * @param p2pInfo provides the p2p connection info. * @param p2pGroup provides the details of the group. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onPeerClientJoined(@NonNull WifiP2pInfo p2pInfo, @NonNull WifiP2pGroup p2pGroup) { } /** * Called to indicate group owner that a group client has disconnected. * * @param p2pInfo provides the p2p connection info. * @param p2pGroup provides the details of the group. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onPeerClientDisconnected(@NonNull WifiP2pInfo p2pInfo, @NonNull WifiP2pGroup p2pGroup) { } /** * Called when the frequency of a formed group has been changed. * * @param p2pInfo provides the p2p connection info. * @param p2pGroup provides the details of the group. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onFrequencyChanged(@NonNull WifiP2pInfo p2pInfo, @NonNull WifiP2pGroup p2pGroup) { } /** * Called when p2p group has been removed. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) default void onGroupRemoved() { } } /** * P2p group creation failed because the connection has been cancelled. * Used in {@link WifiP2pListener#onGroupCreationFailed(int reason)}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int GROUP_CREATION_FAILURE_REASON_CONNECTION_CANCELLED = 0; /** * P2p group creation failed because it has timed out. * Used in {@link WifiP2pListener#onGroupCreationFailed(int reason)}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int GROUP_CREATION_FAILURE_REASON_TIMED_OUT = 1; /** * P2p group creation failed because user has rejected. * Used in {@link WifiP2pListener#onGroupCreationFailed(int reason)}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int GROUP_CREATION_FAILURE_REASON_USER_REJECTED = 2; /** * P2p group creation failed because provision discovery has failed. * Used in {@link WifiP2pListener#onGroupCreationFailed(int reason)}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int GROUP_CREATION_FAILURE_REASON_PROVISION_DISCOVERY_FAILED = 3; /** * P2p group creation failed because the group has been removed. * Used in {@link WifiP2pListener#onGroupCreationFailed(int reason)}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int GROUP_CREATION_FAILURE_REASON_GROUP_REMOVED = 4; /** * P2p group creation failed because invitation has failed. * Used in {@link WifiP2pListener#onGroupCreationFailed(int reason)}. */ @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final int GROUP_CREATION_FAILURE_REASON_INVITATION_FAILED = 5; /** * @hide */ @IntDef(prefix = {"GROUP_CREATION_FAILURE_REASON_"}, value = { GROUP_CREATION_FAILURE_REASON_CONNECTION_CANCELLED, GROUP_CREATION_FAILURE_REASON_TIMED_OUT, GROUP_CREATION_FAILURE_REASON_USER_REJECTED, GROUP_CREATION_FAILURE_REASON_PROVISION_DISCOVERY_FAILED, GROUP_CREATION_FAILURE_REASON_GROUP_REMOVED, GROUP_CREATION_FAILURE_REASON_INVITATION_FAILED }) @Retention(RetentionPolicy.SOURCE) public @interface GroupCreationFailureReason { } /** * Helper class to support wifi p2p listener. */ private static class OnWifiP2pListenerProxy extends IWifiP2pListener.Stub { @NonNull private Executor mExecutor; @NonNull private WifiP2pListener mListener; OnWifiP2pListenerProxy(@NonNull Executor executor, @NonNull WifiP2pListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); mExecutor = executor; mListener = listener; } @Override public void onP2pStateChanged(@WifiP2pState int state) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onP2pStateChanged(state)); } @Override public void onDiscoveryStateChanged(@WifiP2pDiscoveryState int state) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onDiscoveryStateChanged(state)); } @Override public void onListenStateChanged(@WifiP2pListenState int state) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onListenStateChanged(state)); } @Override public void onDeviceConfigurationChanged(WifiP2pDevice p2pDevice) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onDeviceConfigurationChanged(p2pDevice)); } @Override public void onPeerListChanged(WifiP2pDeviceList p2pDeviceList) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onPeerListChanged(p2pDeviceList)); } @Override public void onPersistentGroupsChanged(WifiP2pGroupList p2pGroupList) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onPersistentGroupsChanged(p2pGroupList)); } @Override public void onGroupCreating() { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onGroupCreating()); } @Override public void onGroupNegotiationRejectedByUser() { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onGroupNegotiationRejectedByUser()); } @Override public void onGroupCreationFailed(@GroupCreationFailureReason int reason) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onGroupCreationFailed(reason)); } @Override public void onGroupCreated(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onGroupCreated(p2pInfo, p2pGroup)); } @Override public void onPeerClientJoined(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onPeerClientJoined(p2pInfo, p2pGroup)); } @Override public void onPeerClientDisconnected(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onPeerClientDisconnected(p2pInfo, p2pGroup)); } @Override public void onFrequencyChanged(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup) { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onFrequencyChanged(p2pInfo, p2pGroup)); } @Override public void onGroupRemoved() { Binder.clearCallingIdentity(); mExecutor.execute(() -> mListener.onGroupRemoved()); } } /** * Add a listener to listen to Wi-Fi p2p various changes. * * @param executor the Executor on which to execute the callbacks. * @param listener listener for the Wi-Fi p2p connection changes. * @throws SecurityException if the caller is missing required permissions. * @throws IllegalArgumentException if incorrect input arguments are provided. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_WIFI_STATE }, conditional = true) @RequiresApi(Build.VERSION_CODES.TIRAMISU) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public void registerWifiP2pListener(@NonNull @CallbackExecutor Executor executor, @NonNull WifiP2pListener listener) { Log.d(TAG, "registerWifiP2pListener: listener=" + listener + ", executor=" + executor); final int listenerIdentifier = System.identityHashCode(listener); synchronized (sWifiP2pListenerMap) { try { IWifiP2pListener.Stub listenerProxy = new OnWifiP2pListenerProxy(executor, listener); sWifiP2pListenerMap.put(listenerIdentifier, listenerProxy); Bundle extras = prepareExtrasBundleWithAttributionSource(mContext); mService.registerWifiP2pListener(listenerProxy, mContext.getOpPackageName(), extras); } catch (RemoteException e) { sWifiP2pListenerMap.remove(listenerIdentifier); throw e.rethrowFromSystemServer(); } } } /** * Remove a listener added using * {@link #registerWifiP2pListener(Executor, WifiP2pListener)} * * @param listener the listener to be removed. * @throws IllegalArgumentException if incorrect input arguments are provided. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public void unregisterWifiP2pListener(@NonNull WifiP2pListener listener) { Log.d(TAG, "unregisterWifiP2pListener: listener=" + listener); final int listenerIdentifier = System.identityHashCode(listener); synchronized (sWifiP2pListenerMap) { try { if (!sWifiP2pListenerMap.contains(listenerIdentifier)) { Log.w(TAG, "Unknown external listener " + listenerIdentifier); return; } mService.unregisterWifiP2pListener(sWifiP2pListenerMap.get(listenerIdentifier)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { sWifiP2pListenerMap.remove(listenerIdentifier); } } } /** * A channel that connects the application to the Wifi p2p framework. * Most p2p operations require a Channel as an argument. An instance of Channel is obtained * by doing a call on {@link #initialize} */ public static class Channel implements AutoCloseable { /** @hide */ public Channel(Context context, Looper looper, ChannelListener l, Binder binder, WifiP2pManager p2pManager) { mAsyncChannel = new AsyncChannel(); mHandler = new P2pHandler(looper); mChannelListener = l; mContext = context; mBinder = binder; mP2pManager = p2pManager; mCloseGuard.open("close"); } private final static int INVALID_LISTENER_KEY = 0; private final WifiP2pManager mP2pManager; private ChannelListener mChannelListener; private ServiceResponseListener mServRspListener; private DnsSdServiceResponseListener mDnsSdServRspListener; private DnsSdTxtRecordListener mDnsSdTxtListener; private UpnpServiceResponseListener mUpnpServRspListener; private HashMap mListenerMap = new HashMap(); private final Object mListenerMapLock = new Object(); private int mListenerKey = 0; private final CloseGuard mCloseGuard = new CloseGuard(); /** * Return the binder object. * @hide */ public @NonNull Binder getBinder() { return mBinder; } /** * Close the current P2P connection and indicate to the P2P service that connections * created by the app can be removed. */ public void close() { if (mP2pManager == null) { Log.w(TAG, "Channel.close(): Null mP2pManager!?"); } else { try { mP2pManager.mService.close(mBinder); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } mAsyncChannel.disconnect(); mCloseGuard.close(); Reference.reachabilityFence(this); } /** @hide */ @Override protected void finalize() throws Throwable { try { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } close(); } finally { super.finalize(); } } /* package */ final Binder mBinder; @UnsupportedAppUsage private AsyncChannel mAsyncChannel; private P2pHandler mHandler; Context mContext; class P2pHandler extends Handler { P2pHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message message) { Object listener = null; // The listener for an external approver should be // removed after detaching from the service. switch (message.what) { case EXTERNAL_APPROVER_ATTACH: case EXTERNAL_APPROVER_CONNECTION_REQUESTED: case EXTERNAL_APPROVER_PIN_GENERATED: listener = getListener(message.arg2); break; default: listener = removeListener(message.arg2); break; } switch (message.what) { case AsyncChannel.CMD_CHANNEL_DISCONNECTED: if (mChannelListener != null) { mChannelListener.onChannelDisconnected(); mChannelListener = null; } break; /* ActionListeners grouped together */ case DISCOVER_PEERS_FAILED: case STOP_DISCOVERY_FAILED: case DISCOVER_SERVICES_FAILED: case CONNECT_FAILED: case CANCEL_CONNECT_FAILED: case CREATE_GROUP_FAILED: case REMOVE_GROUP_FAILED: case ADD_LOCAL_SERVICE_FAILED: case REMOVE_LOCAL_SERVICE_FAILED: case CLEAR_LOCAL_SERVICES_FAILED: case ADD_SERVICE_REQUEST_FAILED: case REMOVE_SERVICE_REQUEST_FAILED: case CLEAR_SERVICE_REQUESTS_FAILED: case SET_DEVICE_NAME_FAILED: case DELETE_PERSISTENT_GROUP_FAILED: case SET_WFD_INFO_FAILED: case START_WPS_FAILED: case START_LISTEN_FAILED: case STOP_LISTEN_FAILED: case GET_LISTEN_STATE_FAILED: case SET_CHANNEL_FAILED: case REPORT_NFC_HANDOVER_FAILED: case FACTORY_RESET_FAILED: case SET_ONGOING_PEER_CONFIG_FAILED: case REMOVE_CLIENT_FAILED: case REMOVE_EXTERNAL_APPROVER_FAILED: case SET_CONNECTION_REQUEST_RESULT_FAILED: case SET_VENDOR_ELEMENTS_FAILED: if (listener != null) { ((ActionListener) listener).onFailure(message.arg1); } break; /* ActionListeners grouped together */ case DISCOVER_PEERS_SUCCEEDED: case STOP_DISCOVERY_SUCCEEDED: case DISCOVER_SERVICES_SUCCEEDED: case CONNECT_SUCCEEDED: case CANCEL_CONNECT_SUCCEEDED: case CREATE_GROUP_SUCCEEDED: case REMOVE_GROUP_SUCCEEDED: case ADD_LOCAL_SERVICE_SUCCEEDED: case REMOVE_LOCAL_SERVICE_SUCCEEDED: case CLEAR_LOCAL_SERVICES_SUCCEEDED: case ADD_SERVICE_REQUEST_SUCCEEDED: case REMOVE_SERVICE_REQUEST_SUCCEEDED: case CLEAR_SERVICE_REQUESTS_SUCCEEDED: case SET_DEVICE_NAME_SUCCEEDED: case DELETE_PERSISTENT_GROUP_SUCCEEDED: case SET_WFD_INFO_SUCCEEDED: case START_WPS_SUCCEEDED: case START_LISTEN_SUCCEEDED: case STOP_LISTEN_SUCCEEDED: case SET_CHANNEL_SUCCEEDED: case REPORT_NFC_HANDOVER_SUCCEEDED: case FACTORY_RESET_SUCCEEDED: case SET_ONGOING_PEER_CONFIG_SUCCEEDED: case REMOVE_CLIENT_SUCCEEDED: case REMOVE_EXTERNAL_APPROVER_SUCCEEDED: case SET_CONNECTION_REQUEST_RESULT_SUCCEEDED: case SET_VENDOR_ELEMENTS_SUCCEEDED: if (listener != null) { ((ActionListener) listener).onSuccess(); } break; case RESPONSE_PEERS: WifiP2pDeviceList peers = (WifiP2pDeviceList) message.obj; if (listener != null) { ((PeerListListener) listener).onPeersAvailable(peers); } break; case RESPONSE_CONNECTION_INFO: WifiP2pInfo wifiP2pInfo = (WifiP2pInfo) message.obj; if (listener != null) { ((ConnectionInfoListener) listener).onConnectionInfoAvailable(wifiP2pInfo); } break; case RESPONSE_GROUP_INFO: WifiP2pGroup group = (WifiP2pGroup) message.obj; if (listener != null) { ((GroupInfoListener) listener).onGroupInfoAvailable(group); } break; case RESPONSE_SERVICE: WifiP2pServiceResponse resp = (WifiP2pServiceResponse) message.obj; handleServiceResponse(resp); break; case RESPONSE_PERSISTENT_GROUP_INFO: WifiP2pGroupList groups = (WifiP2pGroupList) message.obj; if (listener != null) { ((PersistentGroupInfoListener) listener). onPersistentGroupInfoAvailable(groups); } break; case RESPONSE_GET_HANDOVER_MESSAGE: Bundle handoverBundle = (Bundle) message.obj; if (listener != null) { String handoverMessage = handoverBundle != null ? handoverBundle.getString(EXTRA_HANDOVER_MESSAGE) : null; ((HandoverMessageListener) listener) .onHandoverMessageAvailable(handoverMessage); } break; case RESPONSE_ONGOING_PEER_CONFIG: WifiP2pConfig peerConfig = (WifiP2pConfig) message.obj; if (listener != null) { ((OngoingPeerInfoListener) listener) .onOngoingPeerAvailable(peerConfig); } break; case RESPONSE_P2P_STATE: if (listener != null) { ((P2pStateListener) listener) .onP2pStateAvailable(message.arg1); } break; case RESPONSE_DISCOVERY_STATE: if (listener != null) { ((DiscoveryStateListener) listener) .onDiscoveryStateAvailable(message.arg1); } break; case RESPONSE_GET_LISTEN_STATE: if (listener != null) { ((ListenStateListener) listener) .onListenStateAvailable(message.arg1); } break; case RESPONSE_NETWORK_INFO: if (listener != null) { ((NetworkInfoListener) listener) .onNetworkInfoAvailable((NetworkInfo) message.obj); } break; case RESPONSE_DEVICE_INFO: if (listener != null) { ((DeviceInfoListener) listener) .onDeviceInfoAvailable((WifiP2pDevice) message.obj); } break; case EXTERNAL_APPROVER_ATTACH: if (listener != null) { ((ExternalApproverRequestListener) listener) .onAttached((MacAddress) message.obj); } break; case EXTERNAL_APPROVER_DETACH: if (listener != null) { ((ExternalApproverRequestListener) listener) .onDetached((MacAddress) message.obj, message.arg1); } break; case EXTERNAL_APPROVER_CONNECTION_REQUESTED: if (listener != null) { int requestType = message.arg1; Bundle bundle = (Bundle) message.obj; WifiP2pDevice device = bundle.getParcelable(EXTRA_PARAM_KEY_DEVICE); WifiP2pConfig config = bundle.getParcelable(EXTRA_PARAM_KEY_CONFIG); ((ExternalApproverRequestListener) listener) .onConnectionRequested(requestType, config, device); } break; case EXTERNAL_APPROVER_PIN_GENERATED: if (listener != null) { Bundle bundle = (Bundle) message.obj; MacAddress deviceAddress = bundle.getParcelable( EXTRA_PARAM_KEY_PEER_ADDRESS); String pin = bundle.getString(EXTRA_PARAM_KEY_WPS_PIN); ((ExternalApproverRequestListener) listener) .onPinGenerated(deviceAddress, pin); } break; default: Log.d(TAG, "Ignored " + message); break; } } } private void handleServiceResponse(WifiP2pServiceResponse resp) { if (resp instanceof WifiP2pDnsSdServiceResponse) { handleDnsSdServiceResponse((WifiP2pDnsSdServiceResponse)resp); } else if (resp instanceof WifiP2pUpnpServiceResponse) { if (mUpnpServRspListener != null) { handleUpnpServiceResponse((WifiP2pUpnpServiceResponse)resp); } } else { if (mServRspListener != null) { mServRspListener.onServiceAvailable(resp.getServiceType(), resp.getRawData(), resp.getSrcDevice()); } } } private void handleUpnpServiceResponse(WifiP2pUpnpServiceResponse resp) { mUpnpServRspListener.onUpnpServiceAvailable(resp.getUniqueServiceNames(), resp.getSrcDevice()); } private void handleDnsSdServiceResponse(WifiP2pDnsSdServiceResponse resp) { if (resp.getDnsType() == WifiP2pDnsSdServiceInfo.DNS_TYPE_PTR) { if (mDnsSdServRspListener != null) { mDnsSdServRspListener.onDnsSdServiceAvailable( resp.getInstanceName(), resp.getDnsQueryName(), resp.getSrcDevice()); } } else if (resp.getDnsType() == WifiP2pDnsSdServiceInfo.DNS_TYPE_TXT) { if (mDnsSdTxtListener != null) { mDnsSdTxtListener.onDnsSdTxtRecordAvailable( resp.getDnsQueryName(), resp.getTxtRecord(), resp.getSrcDevice()); } } else { Log.e(TAG, "Unhandled resp " + resp); } } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int putListener(Object listener) { if (listener == null) return INVALID_LISTENER_KEY; int key; synchronized (mListenerMapLock) { do { key = mListenerKey++; } while (key == INVALID_LISTENER_KEY); mListenerMap.put(key, listener); } return key; } private Object getListener(int key) { if (key == INVALID_LISTENER_KEY) return null; synchronized (mListenerMapLock) { return mListenerMap.get(key); } } private Object removeListener(int key) { if (key == INVALID_LISTENER_KEY) return null; synchronized (mListenerMapLock) { return mListenerMap.remove(key); } } } private static void checkChannel(Channel c) { if (c == null) throw new IllegalArgumentException("Channel needs to be initialized"); } private static void checkServiceInfo(WifiP2pServiceInfo info) { if (info == null) throw new IllegalArgumentException("service info is null"); } private static void checkServiceRequest(WifiP2pServiceRequest req) { if (req == null) throw new IllegalArgumentException("service request is null"); } private void checkP2pConfig(WifiP2pConfig c) { if (c == null) throw new IllegalArgumentException("config cannot be null"); if (TextUtils.isEmpty(c.deviceAddress)) { throw new IllegalArgumentException("deviceAddress cannot be empty"); } } /** * Registers the application with the Wi-Fi framework. This function * must be the first to be called before any p2p operations are performed. * * @param srcContext is the context of the source * @param srcLooper is the Looper on which the callbacks are receivied * @param listener for callback at loss of framework communication. Can be null. * @return Channel instance that is necessary for performing any further p2p operations */ public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) { Binder binder = new Binder(); Bundle extras = prepareExtrasBundleWithAttributionSource(srcContext); int displayId = Display.DEFAULT_DISPLAY; try { Display display = srcContext.getDisplay(); if (display != null) { displayId = display.getDisplayId(); } } catch (UnsupportedOperationException e) { // an acceptable (per API definition) result of getDisplay - implying there's no display // associated with the context } extras.putInt(EXTRA_PARAM_KEY_DISPLAY_ID, displayId); Channel channel = initializeChannel(srcContext, srcLooper, listener, getMessenger(binder, srcContext.getOpPackageName(), extras), binder); mContext = srcContext; return channel; } /** * Registers the application with the Wi-Fi framework. Enables system-only functionality. * @hide */ public Channel initializeInternal(Context srcContext, Looper srcLooper, ChannelListener listener) { return initializeChannel(srcContext, srcLooper, listener, getP2pStateMachineMessenger(), null); } private Message prepareMessage(int what, int arg1, int arg2, Bundle extras, Context context) { Message msg = Message.obtain(); msg.what = what; msg.arg1 = arg1; msg.arg2 = arg2; msg.obj = maybeGetAttributionSource(context); msg.getData().putBundle(EXTRA_PARAM_KEY_BUNDLE, extras); return msg; } private Bundle prepareExtrasBundle(Channel c) { Bundle b = new Bundle(); b.putBinder(CALLING_BINDER, c.getBinder()); return b; } /** * Note, this should only be used for Binder calls. * Unparcelling an AttributionSource will throw an exception when done outside of a Binder * transaction. So don't use this with AsyncChannel since it will throw exception when * unparcelling. */ private Bundle prepareExtrasBundleWithAttributionSource(Context context) { Bundle bundle = new Bundle(); if (SdkLevel.isAtLeastS()) { bundle.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, context.getAttributionSource()); } return bundle; } private Object maybeGetAttributionSource(Context context) { return SdkLevel.isAtLeastS() ? context.getAttributionSource() : null; } private Channel initializeChannel(Context srcContext, Looper srcLooper, ChannelListener listener, Messenger messenger, Binder binder) { if (messenger == null) return null; Channel c = new Channel(srcContext, srcLooper, listener, binder, this); if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger) == AsyncChannel.STATUS_SUCCESSFUL) { Bundle bundle = new Bundle(); bundle.putString(CALLING_PACKAGE, c.mContext.getOpPackageName()); bundle.putString(CALLING_FEATURE_ID, c.mContext.getAttributionTag()); bundle.putBinder(CALLING_BINDER, binder); Message msg = prepareMessage(UPDATE_CHANNEL_INFO, 0, c.putListener(null), bundle, c.mContext); c.mAsyncChannel.sendMessage(msg); return c; } else { c.close(); return null; } } /** * Initiate peer discovery. A discovery process involves scanning for available Wi-Fi peers * for the purpose of establishing a connection. * *

The function call immediately returns after sending a discovery request * to the framework. The application is notified of a success or failure to initiate * discovery through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

The discovery remains active until a connection is initiated or * a p2p group is formed. Register for {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent to * determine when the framework notifies of a change as peers are discovered. * *

Upon receiving a {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent, an application * can request the list of peers using {@link #requestPeers}. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. Can be null. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void discoverPeers(Channel channel, ActionListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); channel.mAsyncChannel.sendMessage(prepareMessage(DISCOVER_PEERS, WIFI_P2P_SCAN_FULL, channel.putListener(listener), extras, channel.mContext)); } /** * Scan only the social channels. * * A discovery process involves scanning for available Wi-Fi peers * for the purpose of establishing a connection. * *

The function call immediately returns after sending a discovery request * to the framework. The application is notified of a success or failure to initiate * discovery through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

The discovery remains active until a connection is initiated or * a p2p group is formed. Register for {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent to * determine when the framework notifies of a change as peers are discovered. * *

Upon receiving a {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent, an application * can request the list of peers using {@link #requestPeers}. *

* The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. *

* Use {@link #isChannelConstrainedDiscoverySupported()} to determine whether the device * supports this feature. If {@link #isChannelConstrainedDiscoverySupported()} return * {@code false} then this method will throw {@link UnsupportedOperationException}. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void discoverPeersOnSocialChannels(@NonNull Channel channel, @Nullable ActionListener listener) { if (!isChannelConstrainedDiscoverySupported()) { throw new UnsupportedOperationException(); } checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); channel.mAsyncChannel.sendMessage(prepareMessage(DISCOVER_PEERS, WIFI_P2P_SCAN_SOCIAL, channel.putListener(listener), extras, channel.mContext)); } /** * Scan only a single channel specified by frequency. * * A discovery process involves scanning for available Wi-Fi peers * for the purpose of establishing a connection. * *

The function call immediately returns after sending a discovery request * to the framework. The application is notified of a success or failure to initiate * discovery through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

The discovery remains active until a connection is initiated or * a p2p group is formed. Register for {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent to * determine when the framework notifies of a change as peers are discovered. * *

Upon receiving a {@link #WIFI_P2P_PEERS_CHANGED_ACTION} intent, an application * can request the list of peers using {@link #requestPeers}. *

* The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. *

* Use {@link #isChannelConstrainedDiscoverySupported()} to determine whether the device * supports this feature. If {@link #isChannelConstrainedDiscoverySupported()} return * {@code false} then this method will throw {@link UnsupportedOperationException}. * * @param channel is the channel created at {@link #initialize} * @param frequencyMhz is the frequency of the channel to use for peer discovery. * @param listener for callbacks on success or failure. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void discoverPeersOnSpecificFrequency( @NonNull Channel channel, int frequencyMhz, @Nullable ActionListener listener) { if (!isChannelConstrainedDiscoverySupported()) { throw new UnsupportedOperationException(); } checkChannel(channel); if (frequencyMhz <= 0) { throw new IllegalArgumentException("This frequency must be a positive value."); } Bundle extras = prepareExtrasBundle(channel); extras.putInt(EXTRA_PARAM_KEY_PEER_DISCOVERY_FREQ, frequencyMhz); channel.mAsyncChannel.sendMessage(prepareMessage(DISCOVER_PEERS, WIFI_P2P_SCAN_SINGLE_FREQ, channel.putListener(listener), extras, channel.mContext)); } /** * Initiate peer discovery. A discovery process involves scanning for available Wi-Fi peers * for the purpose of establishing a connection. See {@link #discoverPeers( * Channel, ActionListener)} for more details. * * This method accepts a {@link WifiP2pDiscoveryConfig} object specifying the desired * parameters for the peer discovery. The configuration object allows the specification of the * scan type (ex. FULL, SOCIAL) and the inclusion of vendor-specific configuration data. * * The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param config is the configuration for this peer discovery * @param listener for callbacks on success or failure. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SuppressLint("ExecutorRegistration") // WifiP2pManager is using the async channel public void startPeerDiscovery( @NonNull Channel channel, @NonNull WifiP2pDiscoveryConfig config, @Nullable ActionListener listener) { if (!isChannelConstrainedDiscoverySupported()) { throw new UnsupportedOperationException(); } checkChannel(channel); Objects.requireNonNull(config); Bundle extras = prepareExtrasBundle(channel); extras.putParcelable(EXTRA_PARAM_KEY_DISCOVERY_CONFIG, config); channel.mAsyncChannel.sendMessage(prepareMessage(DISCOVER_PEERS, WIFI_P2P_SCAN_WITH_CONFIG_PARAMS, channel.putListener(listener), extras, channel.mContext)); } /** * Stop an ongoing peer discovery * *

The function call immediately returns after sending a stop request * to the framework. The application is notified of a success or failure to initiate * stop through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

If P2P Group is in the process of being created, this call will fail (report failure via * {@code listener}. The applicantion should listen to * {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION} to ensure the state is not * {@link android.net.NetworkInfo.State#CONNECTING} and repeat calling when the state changes. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. Can be null. */ public void stopPeerDiscovery(Channel channel, ActionListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, channel.putListener(listener)); } /** * Start a p2p connection to a device with the specified configuration. * *

The function call immediately returns after sending a connection request * to the framework. The application is notified of a success or failure to initiate * connect through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

An app should use {@link WifiP2pConfig.Builder} to build the configuration * for this API, ex. call {@link WifiP2pConfig.Builder#setDeviceAddress(MacAddress)} * to set the peer MAC address and {@link WifiP2pConfig.Builder#enablePersistentMode(boolean)} * to configure the persistent mode. * *

Register for {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION} intent to * determine when the framework notifies of a change in connectivity. * *

If the current device is not part of a p2p group, a connect request initiates * a group negotiation with the peer. * *

If the current device is part of an existing p2p group or has created * a p2p group with {@link #createGroup}, an invitation to join the group is sent to * the peer device. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param config options as described in {@link WifiP2pConfig} class * @param listener for callbacks on success or failure. Can be null. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void connect(Channel channel, WifiP2pConfig config, ActionListener listener) { checkChannel(channel); checkP2pConfig(config); Bundle extras = prepareExtrasBundle(channel); extras.putParcelable(EXTRA_PARAM_KEY_CONFIG, config); channel.mAsyncChannel.sendMessage(prepareMessage(CONNECT, 0, channel.putListener(listener), extras, channel.mContext)); } /** * Cancel any ongoing p2p group negotiation * *

The function call immediately returns after sending a connection cancellation request * to the framework. The application is notified of a success or failure to initiate * cancellation through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. Can be null. */ public void cancelConnect(Channel channel, ActionListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage(CANCEL_CONNECT, 0, channel.putListener(listener)); } /** * Create a p2p group with the current device as the group owner. This essentially creates * an access point that can accept connections from legacy clients as well as other p2p * devices. * *

Note: * This function would normally not be used unless the current device needs * to form a p2p connection with a legacy client * *

The function call immediately returns after sending a group creation request * to the framework. The application is notified of a success or failure to initiate * group creation through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

Application can request for the group details with {@link #requestGroupInfo}. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. Can be null. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void createGroup(Channel channel, ActionListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); channel.mAsyncChannel.sendMessage(prepareMessage(CREATE_GROUP, WifiP2pGroup.NETWORK_ID_PERSISTENT, channel.putListener(listener), extras, channel.mContext)); } /** * Create a p2p group with the current device as the group owner. This essentially creates * an access point that can accept connections from legacy clients as well as other p2p * devices. * *

An app should use {@link WifiP2pConfig.Builder} to build the configuration * for a group. * *

Note: * This function would normally not be used unless the current device needs * to form a p2p group as a Group Owner and allow peers to join it as either * Group Clients or legacy Wi-Fi STAs. * *

The function call immediately returns after sending a group creation request * to the framework. The application is notified of a success or failure to initiate * group creation through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

Application can request for the group details with {@link #requestGroupInfo}. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize}. * @param config the configuration of a p2p group. * @param listener for callbacks on success or failure. Can be null. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void createGroup(@NonNull Channel channel, @Nullable WifiP2pConfig config, @Nullable ActionListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); extras.putParcelable(EXTRA_PARAM_KEY_CONFIG, config); channel.mAsyncChannel.sendMessage(prepareMessage(CREATE_GROUP, 0, channel.putListener(listener), extras, channel.mContext)); } /** * Remove the current p2p group. * *

The function call immediately returns after sending a group removal request * to the framework. The application is notified of a success or failure to initiate * group removal through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. Can be null. */ public void removeGroup(Channel channel, ActionListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage(REMOVE_GROUP, 0, channel.putListener(listener)); } /** * Force p2p to enter listen state. * * When this API is called, this device will periodically enter LISTENING state until * {@link #stopListening(Channel, ActionListener)} or * {@link #stopPeerDiscovery(Channel, ActionListener)} are called. * While in LISTENING state, this device will dwell at its social channel and respond * to probe requests from other Wi-Fi Direct peers. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * @param channel is the channel created at * {@link #initialize(Context, Looper, ChannelListener)} * @param listener for callbacks on success or failure. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void startListening(@NonNull Channel channel, @Nullable ActionListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); channel.mAsyncChannel.sendMessage(prepareMessage(START_LISTEN, 0, channel.putListener(listener), extras, channel.mContext)); } /** * Force P2P to enter the listen state. See {@link #startListening(Channel, ActionListener)} * for more details. * * This method accepts a {@link WifiP2pExtListenParams} object containing additional * parameters. * * @param channel is the channel created at @link #initialize(Context, Looper, ChannelListener)} * @param params are the parameters for this listen request. * @param listener for callbacks on success or failure. * @hide */ @SystemApi @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @SuppressLint("ExecutorRegistration") // WifiP2pManager is using the async channel public void startListening( @NonNull Channel channel, @NonNull WifiP2pExtListenParams params, @Nullable ActionListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); Objects.requireNonNull(params); extras.putParcelable(EXTRA_PARAM_KEY_EXT_LISTEN_PARAMS, params); channel.mAsyncChannel.sendMessage(prepareMessage(START_LISTEN, WIFI_P2P_EXT_LISTEN_WITH_PARAMS, channel.putListener(listener), extras, channel.mContext)); } /** * Force p2p to exit listen state. * * When this API is called, this device will stop entering LISTENING state periodically * which is triggered by {@link #startListening(Channel, ActionListener)}. * If there are running peer discovery which is triggered by * {@link #discoverPeers(Channel, ActionListener)} or running service discovery which is * triggered by {@link #discoverServices(Channel, ActionListener)}, they will be stopped * as well. * * @param channel is the channel created at * {@link #initialize(Context, Looper, ChannelListener)} * @param listener for callbacks on success or failure. */ public void stopListening(@NonNull Channel channel, @Nullable ActionListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage(STOP_LISTEN, 0, channel.putListener(listener)); } /** * Set P2P listening and operating channel. * * @param channel is the channel created at {@link #initialize} * @param listeningChannel the listening channel's Wifi channel number. e.g. 1, 6, 11. * @param operatingChannel the operating channel's Wifi channel number. e.g. 1, 6, 11. * @param listener for callbacks on success or failure. Can be null. * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public void setWifiP2pChannels(@NonNull Channel channel, int listeningChannel, int operatingChannel, @Nullable ActionListener listener) { checkChannel(channel); Bundle p2pChannels = new Bundle(); p2pChannels.putInt("lc", listeningChannel); p2pChannels.putInt("oc", operatingChannel); channel.mAsyncChannel.sendMessage( SET_CHANNEL, 0, channel.putListener(listener), p2pChannels); } /** * Start a Wi-Fi Protected Setup (WPS) session. * *

The function call immediately returns after sending a request to start a * WPS session. Currently, this is only valid if the current device is running * as a group owner to allow any new clients to join the group. The application * is notified of a success or failure to initiate WPS through listener callbacks * {@link ActionListener#onSuccess} or {@link ActionListener#onFailure}. * @hide */ @UnsupportedAppUsage(trackingBug = 185141982) public void startWps(Channel channel, WpsInfo wps, ActionListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage(START_WPS, 0, channel.putListener(listener), wps); } /** * Register a local service for service discovery. If a local service is registered, * the framework automatically responds to a service discovery request from a peer. * *

The function call immediately returns after sending a request to add a local * service to the framework. The application is notified of a success or failure to * add service through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

The service information is set through {@link WifiP2pServiceInfo}.
* or its subclass calls {@link WifiP2pUpnpServiceInfo#newInstance} or * {@link WifiP2pDnsSdServiceInfo#newInstance} for a Upnp or Bonjour service * respectively * *

The service information can be cleared with calls to * {@link #removeLocalService} or {@link #clearLocalServices}. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param servInfo is a local service information. * @param listener for callbacks on success or failure. Can be null. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void addLocalService(Channel channel, WifiP2pServiceInfo servInfo, ActionListener listener) { checkChannel(channel); checkServiceInfo(servInfo); Bundle extras = prepareExtrasBundle(channel); extras.putParcelable(EXTRA_PARAM_KEY_SERVICE_INFO, servInfo); channel.mAsyncChannel.sendMessage(prepareMessage(ADD_LOCAL_SERVICE, 0, channel.putListener(listener), extras, channel.mContext)); } /** * Remove a registered local service added with {@link #addLocalService} * *

The function call immediately returns after sending a request to remove a * local service to the framework. The application is notified of a success or failure to * add service through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * * @param channel is the channel created at {@link #initialize} * @param servInfo is the local service information. * @param listener for callbacks on success or failure. Can be null. */ public void removeLocalService(Channel channel, WifiP2pServiceInfo servInfo, ActionListener listener) { checkChannel(channel); checkServiceInfo(servInfo); channel.mAsyncChannel.sendMessage( REMOVE_LOCAL_SERVICE, 0, channel.putListener(listener), servInfo); } /** * Clear all registered local services of service discovery. * *

The function call immediately returns after sending a request to clear all * local services to the framework. The application is notified of a success or failure to * add service through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. Can be null. */ public void clearLocalServices(Channel channel, ActionListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage(CLEAR_LOCAL_SERVICES, 0, channel.putListener(listener)); } /** * Register a callback to be invoked on receiving service discovery response. * Used only for vendor specific protocol right now. For Bonjour or Upnp, use * {@link #setDnsSdResponseListeners} or {@link #setUpnpServiceResponseListener} * respectively. * *

see {@link #discoverServices} for the detail. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on receiving service discovery response. */ public void setServiceResponseListener(Channel channel, ServiceResponseListener listener) { checkChannel(channel); channel.mServRspListener = listener; } /** * Register a callback to be invoked on receiving Bonjour service discovery * response. * *

see {@link #discoverServices} for the detail. * * @param channel * @param servListener is for listening to a Bonjour service response * @param txtListener is for listening to a Bonjour TXT record response */ public void setDnsSdResponseListeners(Channel channel, DnsSdServiceResponseListener servListener, DnsSdTxtRecordListener txtListener) { checkChannel(channel); channel.mDnsSdServRspListener = servListener; channel.mDnsSdTxtListener = txtListener; } /** * Register a callback to be invoked on receiving upnp service discovery * response. * *

see {@link #discoverServices} for the detail. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on receiving service discovery response. */ public void setUpnpServiceResponseListener(Channel channel, UpnpServiceResponseListener listener) { checkChannel(channel); channel.mUpnpServRspListener = listener; } /** * Initiate service discovery. A discovery process involves scanning for * requested services for the purpose of establishing a connection to a peer * that supports an available service. * *

The function call immediately returns after sending a request to start service * discovery to the framework. The application is notified of a success or failure to initiate * discovery through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

The services to be discovered are specified with calls to {@link #addServiceRequest}. * *

The application is notified of the response against the service discovery request * through listener callbacks registered by {@link #setServiceResponseListener} or * {@link #setDnsSdResponseListeners}, or {@link #setUpnpServiceResponseListener}. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. Can be null. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void discoverServices(Channel channel, ActionListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); channel.mAsyncChannel.sendMessage(prepareMessage(DISCOVER_SERVICES, 0, channel.putListener(listener), extras, channel.mContext)); } /** * Add a service discovery request. * *

The function call immediately returns after sending a request to add service * discovery request to the framework. The application is notified of a success or failure to * add service through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

After service discovery request is added, you can initiate service discovery by * {@link #discoverServices}. * *

The added service requests can be cleared with calls to * {@link #removeServiceRequest(Channel, WifiP2pServiceRequest, ActionListener)} or * {@link #clearServiceRequests(Channel, ActionListener)}. * * @param channel is the channel created at {@link #initialize} * @param req is the service discovery request. * @param listener for callbacks on success or failure. Can be null. */ public void addServiceRequest(Channel channel, WifiP2pServiceRequest req, ActionListener listener) { checkChannel(channel); checkServiceRequest(req); channel.mAsyncChannel.sendMessage(ADD_SERVICE_REQUEST, 0, channel.putListener(listener), req); } /** * Remove a specified service discovery request added with {@link #addServiceRequest} * *

The function call immediately returns after sending a request to remove service * discovery request to the framework. The application is notified of a success or failure to * add service through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * * @param channel is the channel created at {@link #initialize} * @param req is the service discovery request. * @param listener for callbacks on success or failure. Can be null. */ public void removeServiceRequest(Channel channel, WifiP2pServiceRequest req, ActionListener listener) { checkChannel(channel); checkServiceRequest(req); channel.mAsyncChannel.sendMessage(REMOVE_SERVICE_REQUEST, 0, channel.putListener(listener), req); } /** * Clear all registered service discovery requests. * *

The function call immediately returns after sending a request to clear all * service discovery requests to the framework. The application is notified of a success * or failure to add service through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * * @param channel is the channel created at {@link #initialize} * @param listener for callbacks on success or failure. Can be null. */ public void clearServiceRequests(Channel channel, ActionListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage(CLEAR_SERVICE_REQUESTS, 0, channel.putListener(listener)); } /** * Request the current list of peers. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param listener for callback when peer list is available. Can be null. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void requestPeers(Channel channel, PeerListListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); channel.mAsyncChannel.sendMessage(prepareMessage(REQUEST_PEERS, 0, channel.putListener(listener), extras, channel.mContext)); } /** * Request device connection info. * * @param channel is the channel created at {@link #initialize} * @param listener for callback when connection info is available. Can be null. */ public void requestConnectionInfo(Channel channel, ConnectionInfoListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage( REQUEST_CONNECTION_INFO, 0, channel.putListener(listener)); } /** * Request p2p group info. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param listener for callback when group info is available. Can be null. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void requestGroupInfo(Channel channel, GroupInfoListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); channel.mAsyncChannel.sendMessage(prepareMessage(REQUEST_GROUP_INFO, 0, channel.putListener(listener), extras, channel.mContext)); } /** * Set p2p device name. * * @param channel is the channel created at {@link #initialize} * @param listener for callback when group info is available. Can be null. * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public void setDeviceName(@NonNull Channel channel, @NonNull String devName, @Nullable ActionListener listener) { checkChannel(channel); WifiP2pDevice d = new WifiP2pDevice(); d.deviceName = devName; channel.mAsyncChannel.sendMessage(SET_DEVICE_NAME, 0, channel.putListener(listener), d); } /** * Set Wifi Display information. * * @param channel is the channel created at {@link #initialize} * @param wfdInfo the Wifi Display information to set * @param listener for callbacks on success or failure. Can be null. */ @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull Channel channel, @NonNull WifiP2pWfdInfo wfdInfo, @Nullable ActionListener listener) { setWFDInfo(channel, wfdInfo, listener); } /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWFDInfo(@NonNull Channel channel, @NonNull WifiP2pWfdInfo wfdInfo, @Nullable ActionListener listener) { checkChannel(channel); try { mService.checkConfigureWifiDisplayPermission(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } channel.mAsyncChannel.sendMessage(SET_WFD_INFO, 0, channel.putListener(listener), wfdInfo); } /** * Remove the client with the MAC address from the group. * *

The function call immediately returns after sending a client removal request * to the framework. The application is notified of a success or failure to initiate * client removal through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

The callbacks are triggered on the thread specified when initializing the * {@code channel}, see {@link #initialize}. *

* Use {@link #isGroupClientRemovalSupported()} to determine whether the device supports * this feature. If {@link #isGroupClientRemovalSupported()} return {@code false} then this * method will throw {@link UnsupportedOperationException}. * * @param channel is the channel created at {@link #initialize} * @param peerAddress MAC address of the client. * @param listener for callbacks on success or failure. Can be null. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public void removeClient(@NonNull Channel channel, @NonNull MacAddress peerAddress, @Nullable ActionListener listener) { if (!isGroupClientRemovalSupported()) { throw new UnsupportedOperationException(); } checkChannel(channel); channel.mAsyncChannel.sendMessage( REMOVE_CLIENT, 0, channel.putListener(listener), peerAddress); } /** * Delete a stored persistent group from the system settings. * *

The function call immediately returns after sending a persistent group removal request * to the framework. The application is notified of a success or failure to initiate * group removal through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * *

The persistent p2p group list stored in the system can be obtained by * {@link #requestPersistentGroupInfo(Channel, PersistentGroupInfoListener)} and * a network id can be obtained by {@link WifiP2pGroup#getNetworkId()}. * * @param channel is the channel created at {@link #initialize} * @param netId the network id of the p2p group. * @param listener for callbacks on success or failure. Can be null. * * @hide */ @SystemApi @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public void deletePersistentGroup(@NonNull Channel channel, int netId, @Nullable ActionListener listener) { checkChannel(channel); channel.mAsyncChannel.sendMessage( DELETE_PERSISTENT_GROUP, netId, channel.putListener(listener)); } /** * Request a list of all the persistent p2p groups stored in system. * *

The caller must have one of {@link android.Manifest.permission.NETWORK_SETTINGS}, * {@link android.Manifest.permission.NETWORK_STACK}, and * {@link android.Manifest.permission.READ_WIFI_CREDENTIAL}. * *

If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, * the application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param channel is the channel created at {@link #initialize} * @param listener for callback when persistent group info list is available. Can be null. * * @hide */ @SystemApi @RequiresPermission(allOf = { android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.READ_WIFI_CREDENTIAL, android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional = true) public void requestPersistentGroupInfo(@NonNull Channel channel, @Nullable PersistentGroupInfoListener listener) { checkChannel(channel); Bundle extras = prepareExtrasBundle(channel); channel.mAsyncChannel.sendMessage(prepareMessage(REQUEST_PERSISTENT_GROUP_INFO, 0, channel.putListener(listener), extras, channel.mContext)); } /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"MIRACAST_"}, value = { MIRACAST_DISABLED, MIRACAST_SOURCE, MIRACAST_SINK}) public @interface MiracastMode {} /** * Miracast is disabled. * @hide */ @SystemApi public static final int MIRACAST_DISABLED = 0; /** * Device acts as a Miracast source. * @hide */ @SystemApi public static final int MIRACAST_SOURCE = 1; /** * Device acts as a Miracast sink. * @hide */ @SystemApi public static final int MIRACAST_SINK = 2; /** * Accept the incoming request. * * Used in {@link #setConnectionRequestResult(Channel, MacAddress, int, ActionListener)}. */ public static final int CONNECTION_REQUEST_ACCEPT = 0; /** * Reject the incoming request. * * Used in {@link #setConnectionRequestResult(Channel, MacAddress, int, ActionListener)}. */ public static final int CONNECTION_REQUEST_REJECT = 1; /** * Defer the decision back to the Wi-Fi service (which will display a dialog to the user). * * Used in {@link #setConnectionRequestResult(Channel, MacAddress, int, ActionListener)}. */ public static final int CONNECTION_REQUEST_DEFER_TO_SERVICE = 2; /** * Defer the PIN display to the Wi-Fi service (which will display a dialog to the user). * * Used in {@link #setConnectionRequestResult(Channel, MacAddress, int, ActionListener)}. */ public static final int CONNECTION_REQUEST_DEFER_SHOW_PIN_TO_SERVICE = 3; /** @hide */ @IntDef(prefix = {"CONNECTION_REQUEST_"}, value = { CONNECTION_REQUEST_ACCEPT, CONNECTION_REQUEST_REJECT, CONNECTION_REQUEST_DEFER_TO_SERVICE, CONNECTION_REQUEST_DEFER_SHOW_PIN_TO_SERVICE}) @Retention(RetentionPolicy.SOURCE) public @interface ConnectionRequestResponse { } /** * This is used to provide information to drivers to optimize performance depending * on the current mode of operation. * {@link #MIRACAST_DISABLED} - disabled * {@link #MIRACAST_SOURCE} - source operation * {@link #MIRACAST_SINK} - sink operation * * As an example, the driver could reduce the channel dwell time during scanning * when acting as a source or sink to minimize impact on Miracast. * * @param mode mode of operation. One of {@link #MIRACAST_DISABLED}, {@link #MIRACAST_SOURCE}, * or {@link #MIRACAST_SINK} * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setMiracastMode(@MiracastMode int mode) { try { mService.setMiracastMode(mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private Messenger getMessenger(@NonNull Binder binder, @Nullable String packageName, @NonNull Bundle extras) { try { return mService.getMessenger(binder, packageName, extras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get a reference to P2pStateMachine handler. This is used to establish * a priveleged AsyncChannel communication with WifiP2pService. * * @return Messenger pointing to the WifiP2pService handler * @hide */ public Messenger getP2pStateMachineMessenger() { try { return mService.getP2pStateMachineMessenger(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private long getSupportedFeatures() { try { return mService.getSupportedFeatures(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private boolean isFeatureSupported(long feature) { return (getSupportedFeatures() & feature) == feature; } /** * Check if this device supports setting vendor elements. * * Gates whether the * {@link #setVendorElements(Channel, List, ActionListener)} * method is functional on this device. * * @return {@code true} if supported, {@code false} otherwise. */ public boolean isSetVendorElementsSupported() { return isFeatureSupported(FEATURE_SET_VENDOR_ELEMENTS); } /** * Check if this device supports discovery limited to a specific frequency or * the social channels. * * Gates whether * {@link #discoverPeersOnSpecificFrequency(Channel, int, ActionListener)} and * {@link #discoverPeersOnSocialChannels(Channel, ActionListener)} * methods are functional on this device. * * @return {@code true} if supported, {@code false} otherwise. */ public boolean isChannelConstrainedDiscoverySupported() { return isFeatureSupported(FEATURE_FLEXIBLE_DISCOVERY); } /** * Check if this device supports removing clients from a group. * * Gates whether the * {@link #removeClient(Channel, MacAddress, ActionListener)} * method is functional on this device. * @return {@code true} if supported, {@code false} otherwise. */ public boolean isGroupClientRemovalSupported() { return isFeatureSupported(FEATURE_GROUP_CLIENT_REMOVAL); } /** * Checks whether this device, while being a group client, can discover and deliver the group * owner's IPv6 link-local address. * *

If this method returns {@code true} and * {@link #connect(Channel, WifiP2pConfig, ActionListener)} method is called with * {@link WifiP2pConfig} having * {@link WifiP2pConfig#GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL} as the group client * IP provisioning mode, then the group owner's IPv6 link-local address will be delivered in the * group client via {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION} broadcast intent (i.e, group * owner address in {@link #EXTRA_WIFI_P2P_INFO}). * If this method returns {@code false}, then IPv6 link-local addresses can still be used, but * it is the responsibility of the caller to discover that address in other ways, e.g. using * out-of-band communication. * * @return {@code true} if supported, {@code false} otherwise. */ public boolean isGroupOwnerIPv6LinkLocalAddressProvided() { return SdkLevel.isAtLeastT() && isFeatureSupported(FEATURE_GROUP_OWNER_IPV6_LINK_LOCAL_ADDRESS_PROVIDED); } /** * Get a handover request message for use in WFA NFC Handover transfer. * @hide */ public void getNfcHandoverRequest(Channel c, HandoverMessageListener listener) { checkChannel(c); c.mAsyncChannel.sendMessage(GET_HANDOVER_REQUEST, 0, c.putListener(listener)); } /** * Get a handover select message for use in WFA NFC Handover transfer. * @hide */ public void getNfcHandoverSelect(Channel c, HandoverMessageListener listener) { checkChannel(c); c.mAsyncChannel.sendMessage(GET_HANDOVER_SELECT, 0, c.putListener(listener)); } /** * @hide */ public void initiatorReportNfcHandover(Channel c, String handoverSelect, ActionListener listener) { checkChannel(c); Bundle bundle = new Bundle(); bundle.putString(EXTRA_HANDOVER_MESSAGE, handoverSelect); c.mAsyncChannel.sendMessage(INITIATOR_REPORT_NFC_HANDOVER, 0, c.putListener(listener), bundle); } /** * @hide */ public void responderReportNfcHandover(Channel c, String handoverRequest, ActionListener listener) { checkChannel(c); Bundle bundle = new Bundle(); bundle.putString(EXTRA_HANDOVER_MESSAGE, handoverRequest); c.mAsyncChannel.sendMessage(RESPONDER_REPORT_NFC_HANDOVER, 0, c.putListener(listener), bundle); } /** * Removes all saved p2p groups. * * @param c is the channel created at {@link #initialize}. * @param listener for callback on success or failure. Can be null. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull Channel c, @Nullable ActionListener listener) { checkChannel(c); c.mAsyncChannel.sendMessage(FACTORY_RESET, 0, c.putListener(listener)); } /** * Request saved WifiP2pConfig which used for an ongoing peer connection * * @param c is the channel created at {@link #initialize} * @param listener for callback when ongoing peer config updated. Can't be null. * * @hide */ @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void requestOngoingPeerConfig(@NonNull Channel c, @NonNull OngoingPeerInfoListener listener) { checkChannel(c); c.mAsyncChannel.sendMessage(REQUEST_ONGOING_PEER_CONFIG, Binder.getCallingUid(), c.putListener(listener)); } /** * Set saved WifiP2pConfig which used for an ongoing peer connection * * @param c is the channel created at {@link #initialize} * @param config used for change an ongoing peer connection * @param listener for callback when ongoing peer config updated. Can be null. * * @hide */ @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setOngoingPeerConfig(@NonNull Channel c, @NonNull WifiP2pConfig config, @Nullable ActionListener listener) { checkChannel(c); checkP2pConfig(config); c.mAsyncChannel.sendMessage(SET_ONGOING_PEER_CONFIG, 0, c.putListener(listener), config); } /** * Request p2p enabled state. * *

This state indicates whether Wi-Fi p2p is enabled or disabled. * The valid value is one of {@link #WIFI_P2P_STATE_DISABLED} or * {@link #WIFI_P2P_STATE_ENABLED}. The state is returned using the * {@link P2pStateListener} listener. * *

This state is also included in the {@link #WIFI_P2P_STATE_CHANGED_ACTION} * broadcast event with extra {@link #EXTRA_WIFI_STATE}. * * @param c is the channel created at {@link #initialize}. * @param listener for callback when p2p state is available. */ public void requestP2pState(@NonNull Channel c, @NonNull P2pStateListener listener) { checkChannel(c); if (listener == null) throw new IllegalArgumentException("This listener cannot be null."); c.mAsyncChannel.sendMessage(REQUEST_P2P_STATE, 0, c.putListener(listener)); } /** * Request p2p discovery state. * *

This state indicates whether p2p discovery has started or stopped. * The valid value is one of {@link #WIFI_P2P_DISCOVERY_STARTED} or * {@link #WIFI_P2P_DISCOVERY_STOPPED}. The state is returned using the * {@link DiscoveryStateListener} listener. * *

This state is also included in the {@link #WIFI_P2P_DISCOVERY_CHANGED_ACTION} * broadcast event with extra {@link #EXTRA_DISCOVERY_STATE}. * * @param c is the channel created at {@link #initialize}. * @param listener for callback when discovery state is available. */ public void requestDiscoveryState(@NonNull Channel c, @NonNull DiscoveryStateListener listener) { checkChannel(c); if (listener == null) throw new IllegalArgumentException("This listener cannot be null."); c.mAsyncChannel.sendMessage(REQUEST_DISCOVERY_STATE, 0, c.putListener(listener)); } /** * Get p2p listen state. * *

This state indicates whether p2p listen has started or stopped. * The valid value is one of {@link #WIFI_P2P_LISTEN_STOPPED} or * {@link #WIFI_P2P_LISTEN_STARTED}. * *

This state is also included in the {@link #ACTION_WIFI_P2P_LISTEN_STATE_CHANGED} * broadcast event with extra {@link #EXTRA_LISTEN_STATE}. * *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param c It is the channel created at {@link #initialize}. * @param executor The executor on which callback will be invoked. * @param resultsCallback A callback that will return listen state * {@link #WIFI_P2P_LISTEN_STOPPED} or {@link #WIFI_P2P_LISTEN_STARTED} */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void getListenState(@NonNull Channel c, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer resultsCallback) { Objects.requireNonNull(c, "channel cannot be null and needs to be initialized)"); Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); Bundle extras = prepareExtrasBundle(c); c.mAsyncChannel.sendMessage(prepareMessage(GET_LISTEN_STATE, 0, c.putListener(new ListenStateListener() { @Override public void onListenStateAvailable(int state) { Binder.clearCallingIdentity(); executor.execute(() -> { resultsCallback.accept(state); }); } }), extras, c.mContext)); } /** * Request network info. * *

This method provides the network info in the form of a {@link android.net.NetworkInfo}. * {@link android.net.NetworkInfo#isAvailable()} indicates the p2p availability and * {@link android.net.NetworkInfo#getDetailedState()} reports the current fine-grained state * of the network. This {@link android.net.NetworkInfo} is returned using the * {@link NetworkInfoListener} listener. * *

This information is also included in the {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION} * broadcast event with extra {@link #EXTRA_NETWORK_INFO}. * * @param c is the channel created at {@link #initialize}. * @param listener for callback when network info is available. */ public void requestNetworkInfo(@NonNull Channel c, @NonNull NetworkInfoListener listener) { checkChannel(c); if (listener == null) throw new IllegalArgumentException("This listener cannot be null."); c.mAsyncChannel.sendMessage(REQUEST_NETWORK_INFO, 0, c.putListener(listener)); } /** * Request Device Info * *

This method provides the device info * in the form of a {@link android.net.wifi.p2p.WifiP2pDevice}. * Valid {@link android.net.wifi.p2p.WifiP2pDevice} is returned when p2p is enabled. * To get information notifications on P2P getting enabled refers * {@link #WIFI_P2P_STATE_ENABLED}. * *

This {@link android.net.wifi.p2p.WifiP2pDevice} is returned using the * {@link DeviceInfoListener} listener. * *

{@link android.net.wifi.p2p.WifiP2pDevice#deviceAddress} is only available if the caller * holds the {@code android.Manifest.permission#LOCAL_MAC_ADDRESS} permission, and holds the * anonymized MAC address (02:00:00:00:00:00) otherwise. * *

This information is also included in the {@link #WIFI_P2P_THIS_DEVICE_CHANGED_ACTION} * broadcast event with extra {@link #EXTRA_WIFI_P2P_DEVICE}. *

* If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}. * @param listener for callback when network info is available. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION }, conditional = true) public void requestDeviceInfo(@NonNull Channel c, @NonNull DeviceInfoListener listener) { checkChannel(c); if (listener == null) throw new IllegalArgumentException("This listener cannot be null."); Bundle extras = prepareExtrasBundle(c); c.mAsyncChannel.sendMessage(prepareMessage(REQUEST_DEVICE_INFO, 0, c.putListener(listener), extras, c.mContext)); } /** * Set the external approver for a specific peer. * * This API associates a specific peer with an approver. When an incoming request is received * from a peer, an authorization request is routed to the attached approver. The approver then * calls {@link #setConnectionRequestResult(Channel, MacAddress, int, ActionListener)} to send * the result to the WiFi service. A specific peer (identified by its {@code MacAddress}) can * only be attached to a single approver. The previous approver will be detached once a new * approver is attached. The approver will also be detached automatically when the channel is * closed. *

* When an approver is attached, {@link ExternalApproverRequestListener#onAttached(MacAddress)} * is called. When an approver is detached, * {@link ExternalApproverRequestListener#onDetached(MacAddress, int)} is called. * When an incoming request is received, * {@link ExternalApproverRequestListener#onConnectionRequested(int, WifiP2pConfig, WifiP2pDevice)} * is called. When a WPS PIN is generated, * {@link ExternalApproverRequestListener#onPinGenerated(MacAddress, String)} is called. *

* The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}. * @param deviceAddress the peer which is bound to the external approver. * @param listener for callback when the framework needs to notify the external approver. */ @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void addExternalApprover(@NonNull Channel c, @NonNull MacAddress deviceAddress, @NonNull ExternalApproverRequestListener listener) { checkChannel(c); if (listener == null) throw new IllegalArgumentException("This listener cannot be null."); if (null == deviceAddress) { throw new IllegalArgumentException("deviceAddress cannot be empty"); } Bundle extras = prepareExtrasBundle(c); extras.putParcelable(EXTRA_PARAM_KEY_PEER_ADDRESS, deviceAddress); c.mAsyncChannel.sendMessage(prepareMessage(ADD_EXTERNAL_APPROVER, 0, c.putListener(listener), extras, c.mContext)); } /** * Remove the external approver for a specific peer. * * The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}. * @param deviceAddress the peer which is bound to the external approver. * @param listener for callback on success or failure. */ @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void removeExternalApprover(@NonNull Channel c, @NonNull MacAddress deviceAddress, @Nullable ActionListener listener) { checkChannel(c); if (null == deviceAddress) { throw new IllegalArgumentException("deviceAddress cannot be empty"); } Bundle extras = prepareExtrasBundle(c); extras.putParcelable(EXTRA_PARAM_KEY_PEER_ADDRESS, deviceAddress); c.mAsyncChannel.sendMessage(prepareMessage(REMOVE_EXTERNAL_APPROVER, 0, c.putListener(listener), extras, c.mContext)); } /** * Set the result for the incoming request from a specific peer. * * The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}. * @param deviceAddress the peer which is bound to the external approver. * @param result the response for the incoming request. * @param listener for callback on success or failure. */ @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void setConnectionRequestResult(@NonNull Channel c, @NonNull MacAddress deviceAddress, @ConnectionRequestResponse int result, @Nullable ActionListener listener) { checkChannel(c); if (null == deviceAddress) { throw new IllegalArgumentException("deviceAddress cannot be empty"); } Bundle extras = prepareExtrasBundle(c); extras.putParcelable(EXTRA_PARAM_KEY_PEER_ADDRESS, deviceAddress); c.mAsyncChannel.sendMessage(prepareMessage(SET_CONNECTION_REQUEST_RESULT, result, c.putListener(listener), extras, c.mContext)); } /** * Set the result with PIN for the incoming request from a specific peer. * * The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with * android:usesPermissionFlags="neverForLocation". If the application does not declare * android:usesPermissionFlags="neverForLocation", then it must also have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}. * @param deviceAddress the peer which is bound to the external approver. * @param result the response for the incoming request. * @param pin the PIN for the incoming request. * @param listener for callback on success or failure. */ @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void setConnectionRequestResult(@NonNull Channel c, @NonNull MacAddress deviceAddress, @ConnectionRequestResponse int result, @Nullable String pin, @Nullable ActionListener listener) { checkChannel(c); if (null == deviceAddress) { throw new IllegalArgumentException("deviceAddress cannot be empty"); } if (result == CONNECTION_REQUEST_ACCEPT && TextUtils.isEmpty(pin)) { throw new IllegalArgumentException("PIN cannot be empty for accepting a request"); } Bundle extras = prepareExtrasBundle(c); extras.putParcelable(EXTRA_PARAM_KEY_PEER_ADDRESS, deviceAddress); extras.putString(EXTRA_PARAM_KEY_WPS_PIN, pin); c.mAsyncChannel.sendMessage(prepareMessage(SET_CONNECTION_REQUEST_RESULT, result, c.putListener(listener), extras, c.mContext)); } /** * Set/Clear vendor specific information elements (VSIEs) to be published during * Wi-Fi Direct (P2P) discovery. * * Once {@link Channel#close()} is called, the vendor information elements will be cleared from * framework. The information element format is defined in the IEEE 802.11-2016 spec * Table 9-77. *

* To clear the previously set vendor elements, call this API with an empty List. *

* The maximum accumulated length of all VSIEs must be before the limit specified by * {@link #getP2pMaxAllowedVendorElementsLengthBytes()}. *

* To publish vendor elements, this API should be called before peer discovery API, ex. * {@link #discoverPeers(Channel, ActionListener)}. *

* Use {@link #isSetVendorElementsSupported()} to determine whether the device supports * this feature. If {@link #isSetVendorElementsSupported()} return {@code false} then * this method will throw {@link UnsupportedOperationException}. * * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}. * @param vendorElements application information as vendor-specific information elements. * @param listener for callback when network info is available. */ @RequiresPermission(allOf = { android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.OVERRIDE_WIFI_CONFIG }) public void setVendorElements(@NonNull Channel c, @NonNull List vendorElements, @Nullable ActionListener listener) { if (!isSetVendorElementsSupported()) { throw new UnsupportedOperationException(); } checkChannel(c); int totalBytes = 0; for (ScanResult.InformationElement e : vendorElements) { if (e.id != ScanResult.InformationElement.EID_VSA) { throw new IllegalArgumentException("received InformationElement which is not " + "a Vendor Specific IE (VSIE). VSIEs have an ID = 221."); } // Length field is 1 byte. if (e.bytes == null || e.bytes.length > 0xff) { throw new IllegalArgumentException("received InformationElement whose payload " + "size is 0 or greater than 255."); } // The total bytes of an IE is EID (1 byte) + length (1 byte) + payload length. totalBytes += 2 + e.bytes.length; if (totalBytes > WIFI_P2P_VENDOR_ELEMENTS_MAXIMUM_LENGTH) { throw new IllegalArgumentException("received InformationElement whose total " + "size is greater than " + WIFI_P2P_VENDOR_ELEMENTS_MAXIMUM_LENGTH + "."); } } Bundle extras = prepareExtrasBundle(c); extras.putParcelableArrayList(EXTRA_PARAM_KEY_INFORMATION_ELEMENT_LIST, new ArrayList<>(vendorElements)); c.mAsyncChannel.sendMessage(prepareMessage(SET_VENDOR_ELEMENTS, 0, c.putListener(listener), extras, c.mContext)); } /** * Return the maximum total length (in bytes) of all Vendor specific information * elements (VSIEs) which can be set using the * {@link #setVendorElements(Channel, List, ActionListener)}. * * The length is calculated adding the payload length + 2 bytes for each VSIE * (2 bytes: 1 byte for type and 1 byte for length). */ public static int getP2pMaxAllowedVendorElementsLengthBytes() { return WIFI_P2P_VENDOR_ELEMENTS_MAXIMUM_LENGTH; } }