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


Я разрабатываю приложение для Android, основанное на использовании Wifi Direct API. Я зарегистрировался в своем Activity a 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
Я полагал, что любое изменение в списке одноранговых узлов (включение или исключение одного из них в прямой диапазон Wi-Fi) может вызвать BroadcastReceiver. В моем приложении, когда новый узел найден, его имя правильно включено в ListView, но если узел покидает диапазон беспроводной связи (или если я выключаю его интерфейс Wi-Fi), то BroadcastReceiver не вызывается (более конкретно,
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION

Событие не запускается), а имя узла остается в ListView.

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

1 ответ:

Я полагал, что любое изменение в списке одноранговых узлов (включение или исключение одного из них в прямой диапазон Wi-Fi) может привести к запуску BroadcastReceiver.
Да, я думаю, что это должно произойти.

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

Мы можем попробовать покопаться в исходном коде и посмотреть, сможем ли мы объяснить это поведение. Примечание: у меня нет решения, которое можно было бы предложить. Но следующее исследование может помочь вам лучше понять "проблему".

Начнем отсюда.: WifiP2pSettings ссылка

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

Двигаемся дальше-давайте посмотрим, кто транслирует действие 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 ссылка как:

<!-- 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 минуту.

Поскольку

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

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

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