How to be notified when a peer is no longer available in the Wi-Fi Direct range?
Asked Answered
V

1

16

I am developing an Android application based on the use of Wifi Direct API. I have registered in my Activity a BroadcastReceiver in order to be notified about the following Wifi Direct events:

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

I believed that any change in the list of peers (the inclusion or exclusion of a peer in the Wifi Direct range) could trigger the BroadcastReceiver. In my app, when a new peer is found, its name is correctly included in a ListView, but if the peer leaves the wireless range (or if I turn off its Wi-Fi interface), the BroadcastReceiver is not invoked (more specifically, the

WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION

event is not triggered), and the peer name remains in the ListView.

I would like to know if there is any manner to deal with this problem, since the peer names are included in the ListView, but never excluded. I have already thought in re-initialize the Channel and WifiP2pManager instances, but I believe that this will disconnect all peers.

Vamoose answered 12/1, 2014 at 6:50 Comment(9)
Call a peer "stale" unless you've heard about (or from) them recently. Periodically remove stale names.Collimore
cHao, I think that I cannot simply remove them, since the "peer list" update is done when a WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION is detected (this event is invoked asynchronously by the system after a discoverPeers(channel, actionListener) method call). Another problem would be: how long this "garbage collector" should run?Vamoose
I would think the library itself would discard peers it can't see anymore (though it might not emit an event for them). Do you rely solely on the change events to tell you what's going on, or do you occasionally requestPeers()?Collimore
I tried both approaches, but no success... I also have used a Handler to perform a periodic requestPeers() call, but it does not identified any "changes" (in the case, the disconnection of a peer).Vamoose
I don't know what to tell you, then. Without seeing the code, i'm out of guesses. This seems like it should be a common enough problem, though, that it'd already be accounted for.Collimore
cHao, the code is basically the same of the example WiFiDirectDemo. You can reproduce this problem simply running the example. After starting a "Discover" from both devices, the respectively device names will appear in the screen. If you turn off the wifi of one of the devices, and press again the "Discover" button, the device will not disappear! Furthermore, the dialog shown will never be automatically cancelled, since the onPeersAvailable() method from the PeerListListener interface is never called.Vamoose
Hi @Abhishekchoudhary. Unfortunately, I could not solve that problem. Do you have any idea?Vamoose
no ...i dont get any soln for disExtenuate
@Rafael I'm also facing the same problem now. I'm going to start a bounty, maybe someone can help.Angular
B
30

I believed that any change in the list of peers (the inclusion or exclusion of a peer in the Wifi Direct range) could trigger the BroadcastReceiver.

Yes, I think this should happen.

In my app, when a new peer is found, its name is correctly included in a ListView, but if the peer leaves the wireless range (or if I turn off its Wi-Fi interface), the BroadcastReceiver is not invoked (more specifically, the WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION event is not triggered), and the peer name remains in the ListView.

We can try to dig through the source code and see if we can explain this behavior.

Note: I don't have a solution to offer. But, the following research might help you understand the 'issue' better.

We'll start here: WifiP2pSettings Link

This is the fragment you see when you go to Settings > Wifi > Wifi-Direct. If you go through the code, you'll notice that the implementation is quite similar to WifiDirectDemo project - the BroadcastReceiver listens for the same four actions (and two more - one for UI update). The reason why we're looking at this fragment is to check if the demo itself is flawed. But, it looks like the demo is alright.

Moving on - let's see who's broadcasting the action WIFI_P2P_PEERS_CHANGED_ACTION - ultimately, we're interested in finding out why this isn't happening as soon as a device goes offline/out of range. This takes us to WifiP2pService Link.

The method WifiP2pService # sendPeersChangedBroadcast() issues the broadcast:

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);
}

From looking at WifiP2pService, you can tell that sendPeersChangedBroadcast() is called in response to several events. We're interested in this one: WifiMonitor.P2P_DEVICE_LOST_EVENT Link:

....
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) is responsible for sending the message that lands in the above-mentioned case. Go up the chain to find MonitorThread - a static inner class in WifiMonitor. MonitorThread # dispatchEvent(String) calls the handleP2pEvents(String) method.

Finally, something interesting. Look at the run() method of MonitorThread:

private static class MonitorThread extends Thread {

    ....

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

    ....
}            
  • first, we're inside an infinite-loop.
  • second, mWifiNative.waitForEvent() tells me that its probably a blocking call.

These two points put together indicate to me that I am not going to get a prompt response out of this - well, definitely nothing 'instant-like'. The method that we reached by going up the chain - MonitorThread # dispatchEvent(String) - is called from inside this infinite loop.

Let's check if something can support our educated guess:

Take a look at WifiNative class Link, especially the method setScanInterval(int). This method is called from class WifiStateMachine Link. Go through the method 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);
    }
....

Notice the defaultInterval in the first if block. It's requesting R.integer.config_wifi_scan_interval_p2p_connected which is defined in config.xml Link as:

<!-- 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 ms. That's 1 minute. So, if Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS is not set, the scans will be spaced 1 minute apart.

Since, WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS is placed under global settings, we cannot change it. In fact, it can't even be read. Meaning, the only guarantee we have here is that the list of peers will be refreshed in under a minute. The interval can of course vary from brand to brand.

To verify this, I ran the demo on an asus tablet and a dell tablet. Like you said, device coming online was noticed fairly quickly (during the discovery phase). On switching wifi off, I clocked the response. The offline device was taken off the list automatically - with a substantial delay. The dell tablet took close to 60 seconds to notice that asus is offline. Asus on the other hand took 45 seconds.

To me, this seems like a limitation enforced by android. I cannot tell why. I hope that someone here can offer you a solution - possibly take this research and explore further. But I wouldn't be surprised if a solution did not exist (at present).

Berkman answered 6/8, 2014 at 6:55 Comment(1)
A very comprehensive explanation! ThanksWeise

© 2022 - 2024 — McMap. All rights reserved.