Как получить уведомление, когда одноранговый узел больше не доступен в диапазоне Wi-Fi Direct?

Я разрабатываю приложение для Android на основе использования API Wifi Direct. Я зарегистрировался в своем Activity и BroadcastReceiver, чтобы получать уведомления о следующих событиях Wifi Direct:

WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION 
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION 
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION  
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION

Я считал, что любое изменение в списке пиров (включение или исключение пира в диапазоне Wifi Direct) может вызвать ошибку BroadcastReceiver. В моем приложении, когда новый одноранговый узел найден, его имя правильно включается в ListView, но если одноранговый узел покидает зону действия беспроводной сети (или если я отключу его интерфейс Wi-Fi), BroadcastReceiver не вызывается (точнее, в

WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION

событие не срабатывает), а имя однорангового узла остается в файле ListView.

Я хотел бы знать, есть ли способ решить эту проблему, поскольку имена одноранговых узлов включены в ListView, но никогда не исключаются. Я уже подумал о повторной инициализации экземпляров Channel и WifiP2pManager, но я считаю, что это отключит все одноранговые узлы.


person Rafael Fernandes Lopes    schedule 12.01.2014    source источник
comment
Назовите одноранговый узел устаревшим, если только вы не слышали о нем (или от него) в последнее время. Периодически удаляйте устаревшие имена.   -  person cHao    schedule 12.01.2014
comment
cHao, я думаю, что я не могу просто удалить их, так как обновление списка пиров происходит при обнаружении WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION (это событие вызывается системой асинхронно после вызова метода discoverPeers(channel, actionListener)). Другой проблемой может быть: как долго должен работать этот сборщик мусора?   -  person Rafael Fernandes Lopes    schedule 12.01.2014
comment
Я думаю, что сама библиотека будет отбрасывать одноранговые узлы, которых она больше не видит (хотя она может не генерировать для них событие). Вы полагаетесь исключительно на события изменений, чтобы понять, что происходит, или иногда requestPeers()?   -  person cHao    schedule 12.01.2014
comment
Я пробовал оба подхода, но безуспешно... Я также использовал Handler для выполнения периодического вызова requestPeers(), но он не выявил никаких изменений (в данном случае, отключение однорангового узла).   -  person Rafael Fernandes Lopes    schedule 12.01.2014
comment
Тогда я не знаю, что тебе сказать. Не видя кода, я не в догадках. Однако похоже, что это должна быть достаточно распространенная проблема, чтобы ее уже учитывали.   -  person cHao    schedule 12.01.2014
comment
cHao, код в основном такой же, как и в примере WiFiDirectDemo. Вы можете воспроизвести эту проблему, просто запустив пример. После запуска Discover с обоих устройств на экране появятся соответствующие имена устройств. Если вы отключите Wi-Fi одного из устройств и снова нажмете кнопку «Обнаружение», устройство не исчезнет! Кроме того, показанный диалог никогда не будет автоматически отменен, так как метод onPeersAvailable() из интерфейса PeerListListener никогда не вызывается.   -  person Rafael Fernandes Lopes    schedule 13.01.2014
comment
Привет @Abhishekchoudhary. К сожалению, я не смог решить эту проблему. Есть ли у вас какие-либо идеи?   -  person Rafael Fernandes Lopes    schedule 21.03.2014
comment
нет ... я не получаю ничего за это   -  person Abhishek choudhary    schedule 23.03.2014
comment
@ Рафаэль, я тоже столкнулся с той же проблемой. Я собираюсь начать баунти, может кто-то может помочь.   -  person Karim    schedule 05.08.2014


Ответы (1)


Я полагал, что любое изменение в списке пиров (включение или исключение пира в диапазоне Wifi Direct) может вызвать срабатывание BroadcastReceiver.

Да, я думаю, это должно произойти.

В моем приложении при обнаружении нового однорангового узла его имя правильно включается в ListView, но если одноранговый узел покидает зону действия беспроводной сети (или если я отключу его интерфейс Wi-Fi), BroadcastReceiver не вызывается (точнее, событие WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION не запускается), а имя однорангового узла остается в ListView.

Мы можем попытаться покопаться в исходном коде и посмотреть, сможем ли мы объяснить такое поведение.

Примечание. У меня нет решения, которое я мог бы предложить. Но следующее исследование может помочь вам лучше понять «проблему».

Мы начнем здесь: WifiP2pSettings Ссылка

Это фрагмент, который вы видите, когда выбираете Настройки > Wifi > Wifi-Direct. Если вы просмотрите код, вы заметите, что реализация очень похожа на проект WifiDirectDemoBroadcastReceiver прослушивает те же четыре действия (и еще два — одно для обновления пользовательского интерфейса). Причина, по которой мы смотрим на этот фрагмент, состоит в том, чтобы проверить, не является ли сама демонстрация ошибочной. Но, похоже, демо в порядке.

Двигаемся дальше — давайте посмотрим, кто транслирует действие WIFI_P2P_PEERS_CHANGED_ACTION — в конечном счете, нам интересно выяснить, почему это не происходит, когда устройство отключается или выходит за пределы диапазона. Это приводит нас к WifiP2pService Ссылка.

Метод WifiP2pService # sendPeersChangedBroadcast() выдает широковещательную рассылку:

private void sendPeersChangedBroadcast() {
    final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    intent.putExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, new WifiP2pDeviceList(mPeers));
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}

Глядя на WifiP2pService, вы можете сказать, что sendPeersChangedBroadcast() вызывается в ответ на несколько событий. Нас интересует вот это: WifiMonitor.P2P_DEVICE_LOST_EVENT Ссылка:

....
case WifiMonitor.P2P_DEVICE_LOST_EVENT:
    device = (WifiP2pDevice) message.obj;
    // Gets current details for the one removed
    device = mPeers.remove(device.deviceAddress);
    if (device != null) {
        sendPeersChangedBroadcast();
    }
    break;
....

WifiMonitor # handleP2pEvents(String) отвечает за отправку сообщения, которое попадает в вышеупомянутый case. Поднимитесь по цепочке, чтобы найти MonitorThread — статический внутренний класс в WifiMonitor. MonitorThread # dispatchEvent(String) вызывает метод handleP2pEvents(String).

Наконец, что-то интересное. Посмотрите на метод run() MonitorThread:

private static class MonitorThread extends Thread {

    ....

    public void run() {
        //noinspection InfiniteLoopStatement
        for (;;) {
            String eventStr = mWifiNative.waitForEvent();
            ....
        }
    }

    ....
}            
  • во-первых, мы внутри бесконечного цикла.
  • во-вторых, mWifiNative.waitForEvent() говорит мне, что это вероятно блокирующий вызов.

Эти два пункта вместе взятые указывают мне на то, что я не получу от этого быстрого ответа — ну, определенно ничего «мгновенного». Метод, которого мы достигли, поднявшись по цепочке — MonitorThread # dispatchEvent(String) — вызывается изнутри этого бесконечного цикла.

Давайте проверим, может ли что-то подтвердить наше обоснованное предположение:

Взгляните на класс WifiNative Ссылка, особенно метод setScanInterval(int). Этот метод вызывается из класса WifiStateMachine Ссылка. Пройдите метод processMessage(Message):

....
case WifiP2pService.P2P_CONNECTION_CHANGED:
    NetworkInfo info = (NetworkInfo) message.obj;
    mP2pConnected.set(info.isConnected());
    if (mP2pConnected.get()) {
        int defaultInterval = mContext.getResources().getInteger(
                            R.integer.config_wifi_scan_interval_p2p_connected);
        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
                            Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
                            defaultInterval);

        // ====>> Interval defined here
        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
        if (DBG) log("Turn on scanning after p2p disconnected");
            sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
                            ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
    }
....

Обратите внимание на defaultInterval в первом if block. Он запрашивает R.integer.config_wifi_scan_interval_p2p_connected, который определен в config.xml Link как:

<!-- Integer indicating wpa_supplicant scan interval when p2p is connected in milliseconds -->
<integer translatable="false" name="config_wifi_scan_interval_p2p_connected">60000</integer>

60000 мс. Это 1 минута. Таким образом, если Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS не задано, интервал сканирования будет составлять 1 минуту.

Поскольку WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS находится в глобальных настройках, мы не можем его изменить. На самом деле, это даже невозможно прочитать. Это означает, что единственная гарантия, которую мы здесь имеем, заключается в том, что список одноранговых узлов будет обновлен менее чем за минуту. Интервал, конечно, может варьироваться от бренда к бренду.

Чтобы убедиться в этом, я запустил демоверсию на планшетах asus и dell. Как вы сказали, подключение устройства к сети было замечено довольно быстро (на этапе обнаружения). При выключении Wi-Fi я синхронизировал ответ. Автономное устройство было удалено из списка автоматически со значительной задержкой. Планшету Dell потребовалось около 60 seconds, чтобы заметить, что asus находится в автономном режиме. Asus, с другой стороны, взял 45 seconds.

Мне кажется, что это ограничение, налагаемое Android. Я не могу сказать, почему. Я надеюсь, что кто-то здесь может предложить вам решение - возможно, проведите это исследование и изучите его дальше. Но я не удивлюсь, если решения не существует (в настоящее время).

person Vikram    schedule 06.08.2014
comment
Очень исчерпывающее объяснение! Спасибо - person Ali.DM; 21.12.2019