Использование Wi-Fi P2P для обнаружения сервисов

Первый урок в этом курсе, Использование обнаружения сетевых сервисов, показал как обнаружить сервисы, которые подключены к локальной сети. Однако, использование Обнаружение сервисов Wi-Fi Peer-To-Peer (P2P) позволяет обнаружить службы близлежащих устройств напрямую, без подключения к сети. Вы также можете информировать о своих сервисах, запущенных на вашем устройстве. Эти возможности помогут вам общаться между приложениями, даже при отсутствии локальной сети или точки доступа.

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

Настройка манифеста

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

<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"/>
    ...

Добавление локального сервиса

Если вы предоставляете локальную службу, необходимо зарегистрировать её для возможности обнаружения. После того, как ваш локальный сервис зарегистрирован, платформа автоматически реагирует на обслуживание запросов обнаружения со стороны близлежащих устройств.

Чтобы создать локальную службу:

  1. Создайте WifiP2pServiceInfo объект.
  2. Заполните его информацией о вашей службе.
  3. Вызовите addLocalService() для регистрации локальной службы для возможности её обнаружения.
     private void startRegistration() {
        //  Create a string map containing information about your service.
        Map
       
         record = new HashMap
        
         (); record.put("listenport", String.valueOf(SERVER_PORT)); record.put("buddyname", "John Doe" + (int) (Math.random() * 1000)); record.put("available", "visible"); // Service information. Pass it an instance name, service type // _protocol._transportlayer , and the map containing // information other devices will want once they connect to this one. WifiP2pDnsSdServiceInfo serviceInfo = WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record); // Add the local service, sending the service info, network channel, // and listener that will be used to indicate success or failure of // the request. mManager.addLocalService(channel, serviceInfo, new ActionListener() { @Override public void onSuccess() { // Command successful! Code isn't necessarily needed here, // Unless you want to update the UI or add logging statements. } @Override public void onFailure(int arg0) { // Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY } }); } 
        
       

Обнаружение ближайших служб

Android использует методы обратного вызова для уведомления приложения о доступных службах, поэтому первое, что нужно сделать, это реализовать их. Создайте WifiP2pManager.DnsSdTxtRecordListener для прослушивания входящих регистраций. Эта регистрация может быть отправлена с другого устройства. Когда вам приходит регистрация, скопируйте адрес устройства и любую другую соответствующую информацию во внешнюю по отношению к текущему методу структуру данных, так что вы сможете получить доступ к ней позже. В следующем примере предполагается, что регистрация содержит поле "buddyname", заполненное информацией о пользователе.

final HashMap<String, String> buddies = new HashMap<String, String>();
...
private void discoverService() {
    DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
        @Override
        /* Callback includes:
         * fullDomain: full domain name: e.g "printer._ipp._tcp.local."
         * record: TXT record dta as a map of key/value pairs.
         * device: The device running the advertised service.
         */

        public void onDnsSdTxtRecordAvailable(
                String fullDomain, Map
       
         record, WifiP2pDevice device) {
                Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
                buddies.put(device.deviceAddress, record.get("buddyname"));
            }
        };
    ...
}

       

Для получения информации о сервисе, создайте WifiP2pManager.DnsSdServiceResponseListener. Он получает фактическое описание и информацию о соединении. В предыдущем фрагменте кода используется Map объект для хранения пар адреса устройства и значения поля buddyname. Обработчик ответов служб использует это, чтобы связать DNS запись с соответствующей служебной информацией. Как только оба обработчика будут реализованы, добавьте их в WifiP2pManager с помощью setDnsSdResponseListeners() метода.

private void discoverService() {
...

    DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
        @Override
        public void onDnsSdServiceAvailable(String instanceName, String registrationType,
                WifiP2pDevice resourceType) {

                // Update the device name with the human-friendly version from
                // the DnsTxtRecord, assuming one arrived.
                resourceType.deviceName = buddies
                        .containsKey(resourceType.deviceAddress) ? buddies
                        .get(resourceType.deviceAddress) : resourceType.deviceName;

                // Add to the custom adapter defined specifically for showing
                // wifi devices.
                WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager()
                        .findFragmentById(R.id.frag_peerlist);
                WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment
                        .getListAdapter());

                adapter.add(resourceType);
                adapter.notifyDataSetChanged();
                Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
        }
    };

    mManager.setDnsSdResponseListeners(channel, servListener, txtListener);
    ...
}

Теперь создайте запрос и вызовите addServiceRequest(). Этот метод также принимает обработчик события об успехе или неудаче.

        serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
        mManager.addServiceRequest(channel,
                serviceRequest,
                new ActionListener() {
                    @Override
                    public void onSuccess() {
                        // Success!
                    }

                    @Override
                    public void onFailure(int code) {
                        // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
                    }
                });

В заключение, сделайте вызов discoverServices().

        mManager.discoverServices(channel, new ActionListener() {

            @Override
            public void onSuccess() {
                // Success!
            }

            @Override
            public void onFailure(int code) {
                // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
                if (code == WifiP2pManager.P2P_UNSUPPORTED) {
                    Log.d(TAG, "P2P isn't supported on this device.");
                else if(...)
                    ...
            }
        });

Если все пойдет хорошо, ура, все готово! Если у вас возникли проблемы, помните, что асинхронные вызовы, которые вы делаете, принимают WifiP2pManager.ActionListener в качестве аргумента, который предоставляет методы обратного вызова с указанием успеха или неудачи. Для диагностики проблем, положить отладочный код в onFailure(). Код ошибки предоставляемый методом указывает на проблему. Вот возможные значения ошибок и описание

P2P_UNSUPPORTED
Wi-Fi P2P не поддерживается устройством, выполняющим приложение.
BUSY
Система занята и не может обработать запрос.
ERROR
Операция не удалась из-за внутренней ошибки.