Создание P2P подключений через Wi-Fi

API для одноранговых (P2P) Wi-Fi подключений позволяют приложениям подключаться к соседним устройствам без необходимости подключения к сети или точке доступа (Wi-Fi P2P платформы Android соответствует программе сертификации Wi-Fi Direct™ ). Wi-Fi P2P позволяет приложению быстро находить и взаимодействовать с другими устройствами, в диапазоне за пределами возможностей Bluetooth.

Этот урок покажет вам, как найти и подключиться к близлежащему устройству, используя Wi-Fi P2P.

Настройка разрешений приложения

Чтобы использовать Wi-Fi P2P, добавьте CHANGE_WIFI_STATE, ACCESS_WIFI_STATE, и INTERNET разрешения в манифест. Wi-Fi P2P не требует подключения к интернету, но оно использует стандартные Java сокеты, которые требуют INTERNET разрешение. Поэтому вам нужны следующие разрешения для использования Wi-Fi P2P.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.nsdchat"
    ...

    <uses-permission
        android:required="true"
        android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission
        android:required="true"
        android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission
        android:required="true"
        android:name="android.permission.INTERNET"/>
    ...

Настройка приемника широковещательных запросов и P2P менеджера

Для использования Wi-Fi P2P, вы должны слушать рассылку намерений, которые говорят вашему приложению, когда произошли определенные события. В вашем приложении, создайте экземпляр IntentFilter и настройте его для прослушивания следующего:

WIFI_P2P_STATE_CHANGED_ACTION
Показывает включен ли Wi-Fi P2P
WIFI_P2P_PEERS_CHANGED_ACTION
Указывает, что список доступных узлов изменился.
WIFI_P2P_CONNECTION_CHANGED_ACTION
Указывает, что состояние Wi-Fi P2P соединения изменилось.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
Указывает, что детали конфигурации этого устройства изменились.
private final IntentFilter intentFilter = new IntentFilter();
...
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    //  Indicates a change in the Wi-Fi P2P status.
    intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);

    // Indicates a change in the list of available peers.
    intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);

    // Indicates the state of Wi-Fi P2P connectivity has changed.
    intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);

    // Indicates this device's details have changed.
    intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

    ...
}

В конце onCreate() метода, получить экземпляр WifiP2pManager, и вызовите его initialize() метод. Этот метод возвращает WifiP2pManager.Channel объект, который вы будете использовать позже для подключения вашего приложения к Wi-Fi P2P платформе.

@Override

Channel mChannel;

public void onCreate(Bundle savedInstanceState) {
    ....
    mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
    mChannel = mManager.initialize(this, getMainLooper(), null);
}

Теперь создайте новый BroadcastReceiver класс, который вы будете использовать для прослушивания изменений состояния системы Wi-Fi P2P. В onReceive() методе, добавьте условие для обработки каждого изменения состояния P2P перечисленных выше.


    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
            // Determine if Wifi P2P mode is enabled or not, alert
            // the Activity.
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                activity.setIsWifiP2pEnabled(true);
            } else {
                activity.setIsWifiP2pEnabled(false);
            }
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

            // The peer list has changed!  We should probably do something about
            // that.

        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

            // Connection state changed!  We should probably do something about
            // that.

        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
                    .findFragmentById(R.id.frag_list);
            fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
                    WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));

        }
    }

В конце, добавьте код для регистрации фильтра намерений и обработчика широковещательный запросов, когда ваша основная деятельность является активной, и отменяет их регистрацию, когда деятельность приостанавливается. Лучшее место сделать это в onResume() и onPause() методах.

    /** register the BroadcastReceiver with the intent values to be matched */
    @Override
    public void onResume() {
        super.onResume();
        receiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
        registerReceiver(receiver, intentFilter);
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(receiver);
    }

Инициирование обнаружения устройств

Чтобы начать поиск близлежащих устройств с Wi-Fi P2P, вызовите discoverPeers(). Этот метод имеет следующие аргументы:

  • WifiP2pManager.Channel , который вы получили обратно при инициализации P2P mManager
  • Реализация WifiP2pManager.ActionListener с методами, которые система вызывает для успешного и неудачного обнаружения.
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {

        @Override
        public void onSuccess() {
            // Code for when the discovery initiation is successful goes here.
            // No services have actually been discovered yet, so this method
            // can often be left blank.  Code for peer discovery goes in the
            // onReceive method, detailed below.
        }

        @Override
        public void onFailure(int reasonCode) {
            // Code for when the discovery initiation fails goes here.
            // Alert the user that something went wrong.
        }
});

Имейте в виду, что это только инициирует обнаружение узлов. discoverPeers() метод запускает процесс обнаружения и затем сразу же возвращает управление. Система уведомляет вас, когда процесс обнаружения узлов успешно инициирован посредством вызова методов предоставленного обработчика действий. Кроме того, обнаружение будет оставаться активным, пока не будет установлено соединение или пока не будет образована P2P группа.

Выборка списка узлов

Теперь напишем код, который извлекает и обрабатывает список узлов сети. Вначале реализуйте WifiP2pManager.PeerListListener интерфейс, который предоставляет информацию об узлах, которые обнаружил Wi-Fi P2P. Это иллюстрирует следующий фрагмент кода.

    private List
        
          peers = new ArrayList
         
          (); ... private PeerListListener peerListListener = new PeerListListener() { @Override public void onPeersAvailable(WifiP2pDeviceList peerList) { // Out with the old, in with the new. peers.clear(); peers.addAll(peerList.getDeviceList()); // If an AdapterView is backed by this data, notify it // of the change. For instance, if you have a ListView of available // peers, trigger an update. ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged(); if (peers.size() == 0) { Log.d(WiFiDirectActivity.TAG, "No devices found"); return; } } } 
         
        

Теперь измените метод вашего широковещательного приемника onReceive() и вызовите requestPeers() когда получено намерение с действием WIFI_P2P_PEERS_CHANGED_ACTION . Вам необходимо как-то передать этот обработчик в приемник. Один из способов это передать его в качестве аргумента в конструктор широковещательного приемника.

public void onReceive(Context context, Intent intent) {
    ...
    else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

        // Request available peers from the wifi p2p manager. This is an
        // asynchronous call and the calling activity is notified with a
        // callback on PeerListListener.onPeersAvailable()
        if (mManager != null) {
            mManager.requestPeers(mChannel, peerListListener);
        }
        Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
    }...
}

Теперь, намерение с действием WIFI_P2P_PEERS_CHANGED_ACTION запустит запрос на обновленный списка узлов.

Подключение к узлу

Для подключения к узлу, создайте новый WifiP2pConfig объект, и скопируйте в него данные из WifiP2pDevice , представляющий устройство, к которому необходимо подключиться. Затем вызовите connect() метод.

    @Override
    public void connect() {
        // Picking the first device found on the network.
        WifiP2pDevice device = peers.get(0);

        WifiP2pConfig config = new WifiP2pConfig();
        config.deviceAddress = device.deviceAddress;
        config.wps.setup = WpsInfo.PBC;

        mManager.connect(mChannel, config, new ActionListener() {

            @Override
            public void onSuccess() {
                // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
            }

            @Override
            public void onFailure(int reason) {
                Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

WifiP2pManager.ActionListener реализованный в этом фрагменте уведомляет вас только когда инициирование завершилось успешно или потерпело неудачу. Чтобы прослушивать изменения состояния соединения, реализуйте WifiP2pManager.ConnectionInfoListener интерфейс. Его onConnectionInfoAvailable() метод обратного вызова сообщает вам, когда изменяется состояние подключения. В случаях, когда вы хотите подключить несколько устройств к одному устройству (например, игры с 3 или более игроков, или чат-приложение), одно устройство будет назначено "владельцем группы".

    @Override
    public void onConnectionInfoAvailable(final WifiP2pInfo info) {

        // InetAddress from WifiP2pInfo struct.
        InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress());

        // After the group negotiation, we can determine the group owner.
        if (info.groupFormed && info.isGroupOwner) {
            // Do whatever tasks are specific to the group owner.
            // One common case is creating a server thread and accepting
            // incoming connections.
        } else if (info.groupFormed) {
            // The other device acts as the client. In this case,
            // you'll want to create a client thread that connects to the group
            // owner.
        }
    }

Теперь вернемся к onReceive() методу широковещательного приемника, и изменим раздел, который ожидает WIFI_P2P_CONNECTION_CHANGED_ACTION намерение. Когда это намерение будет получено, вызовите requestConnectionInfo(). Это асинхронный вызов, поэтому результаты будут получены обработчиком информации о подключении, который вы передали в качестве параметра.

        ...
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

            if (mManager == null) {
                return;
            }

            NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);

            if (networkInfo.isConnected()) {

                // We are connected with the other device, request connection
                // info to find group owner IP

                mManager.requestConnectionInfo(mChannel, connectionListener);
            }
            ...