What is causing multicast messages not to flow immediately after wifi restart - android

I have an Android app that creates a MulticastSocket, joins a MC group and receives messages from another machine on the local wifi network.
MulticastSocket socket = new MulticastSocket(null); // Create an unbound socket.
socket.setSoTimeout(LISTEN_TIMEOUT_MILLIS);
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(listenPort)); // Bind to the configured multicast port
final WifiManager.MulticastLock lock = wifiManager.createMulticastLock("my_lock");
lock.acquire();
socket.setNetworkInterface(networkInterface);
socket.joinGroup(multicastGroup);
while (true) {
socket.receive(packet);
// Do something with the packet
// Handle timeout etc.
// Handle change of network interface by leaving group, setting netIntf and joining group again.
}
socket.leaveGroup(multicastGroup);
socket.close();
lock.release();
Works well on most Android devices (Huawei, Samsung), but on some (Pixel3), if the WiFi on the device is switched off and then back on again, while the app sees the Wifi connection come live, it can take up to 14 mins (it is extremely variable) before the MC messages start being received again.
Even throwing away the Socket and creating a fresh MCSocket doesn't alleviate the delay.
But it has to be some state that is held within the JVM, because a restart of the app causes it to connect immediately.
It feels like there is some lease that is being held for the MC connection that is only being renewed on a clock cycle.
So my questions are:
What is causing the MC messages to not flow immediately after the
WiFi connection comes back up and a new MCSocket is created to
listen to it.
What can I do to ensure timely resumption of the message flow?

I notice you've updated your question to include WifiManager.MulticastLock
I wonder if you are you reacquiring the lock when the Wifi connection comes back, some posts here on SO imply this is necessary.
I note the comment on the following post:
Re: https://stackoverflow.com/a/4002084/1015289
it turns out that your multicast lock is destroyed when the connectivity goes away (the long delay was me rewriting my code three times before I figured this out). So, you have to reacquire the lock every time the connection comes back

Related

Android: Catching BLE Connection Fails/Disconnects?

So I'm able to connect to a BLE device just fine under normal circumstances. What I want to do is handle abnormal circumstances, like when the connection to a device fails or an established connection is lost (maybe it got thrown off a cliff or hit by a bus)
I'm using a CyPress BLE module to test this, and one of the tests I'm doing is to remove power from the module. However, onConnectionStateChange never gets called! All I ever see it respond to is successful connections. It'll spend hours trying to connect and never give up evidently. I would do a delayed cancellation of the connection attempt, but there is no way to cancel the connection attempt on a Bluetooth device (that I know of)! As far as I can tell it'll keep trying until the battery runs down.
Here's what my onConnectionStateChange looks like right now, inside the Gatt Callback. Note that I'm trying to catch and log ANY kind of callback involving ANY kind of connection state change... which never gets called unless connection success. Note that this is code is not on the activity itself. It's in an object held by a singleton. (I want to maintain connection across multiple activities)
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
mGatt = gatt;
Logy.CallPrint(LOGY_ENABLE, CLASSNAME, "Status: "+status+ " Newstate: "+newState);
switch(status)
{
case BluetoothGatt.GATT_SUCCESS:
mReconnectAttempts = MAX_ATTEMPTS;
if(newState == BluetoothGatt.STATE_CONNECTED)
{
DispatchEvent(Event.Type.BT_ON_CONNECT);
bIsConnected = true;
gatt.discoverServices();
} else if (newState == BluetoothGatt.STATE_DISCONNECTED)
{
DispatchEvent(Event.Type.BT_ON_DISCONNECT);
bIsConnected = false;
}
break;
default:
if(newState == BluetoothGatt.STATE_DISCONNECTED)
{
bIsConnected = false;
if(mReconnectAttempts > 0)
{ // if we have attempts left, auto attempt to reconnect
DispatchEvent(Event.Type.BT_RECONNECTING);
mReconnectAttempts--;
gatt.connect();
bIsConnected = false;
}
else
{
mReconnectAttempts = MAX_ATTEMPTS;
DispatchEvent(Event.Type.BT_ON_CONNECT_FAIL);
bIsConnected = false;
}
} else {
Logy.CallPrint(LOGY_ENABLE, CLASSNAME, "onConnectionStateChange: Failed?");
}
}
super.onConnectionStateChange(gatt, status, newState);
}
Not being able to detect disconnects is an issue elsewhere in my code, like where I show a Progress Dialog indicating the app is connecting to a BLE device. Well, that dialog never goes away because the event "On Connect Fail" never gets thrown.
I think what you are looking for is Bluetooth Supervision timeout which is according to Bluetooth LE specifications :
a parameter that defines the maximum time between two received Data Packet PDUs before the connection is considered lost
Default Supervision timeoout is set to 20 seconds on Android (depending on Android version & device). For instance, here is the value of Supervision Timeout on Android 5.1.
There is no API to set this parameter, so you will have to wait 20 seconds (depending on your Android version & device) to get onConnectionStateChange callback with status BluetoothGatt.STATE_DISCONNECTED after you power off your BLE module
This answer aligns with the answer from Emil.
I coded up a test App with a single Activity running on Moto G4 Play Android 6.0.1 (Marshmellow: API23) and a Peripheral based on a Laird BL600
I was going to post some logs - but they don't format so well - so I'll just describe the results.
As usual, the Peripheral advertised and the Central scanned, and obtained a device instance.
The question does not state exactly how the first connection is made, but let's assume it is with 'autoconnect' false, of the form
mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
(this is the only style of connection used in the test App)
This gives an instance of BluetoothGatt and, as noted in the question, events are then reported asynchronously through the call back,
but there is also an instance of BluetoothGatt on which to call disconnect() even before any connection events have occurred.
The API documentation for disconnect() states - especially the part after the ','
BluetoothGatt.disconnect()
Disconnects an established connection, or cancels a connection attempt currently in progress.
To free up GATT resources, after disconnecting, the test App also then always calls
mBluetoothGatt.close();
Here are some different scenarios
device.connectGatt() -- Peripheral advertising - connection made -- followed by mBluetoothGatt.disconnect() and close().
Result: this is a normal successful connection, followed by Central device closing the connection in code. Call back events are received as expected. When the Central disconnects in the code, the underlying Bluetooth Service disconnects and the Peripheral gets a Disconnection event.
.
device.connectGatt() -- Peripheral advertising - connection made - followed by Peripheral switched off.
Result: this is a normal successful connection, call back events as expected, followed by connection broken by Peripheral. The Peripheral indicates preferred connection supervision timeout in BleGapSvcInit and this is confirmed by Central in a Connection Parameters Update. After Peripheral drops the connection a Disconnection event occurs in Central just after the connection supervision timeout. (Repeated with 8 seconds and 4 seconds supervision timeouts).
.
device.connectGatt() -- but Peripheral has stopped advertising, connection attempt allowed to time out.
Result: (This is a specific scenario of the question) After 30 seconds, probably the connection timeout of the Bluetooth Service on the phone,
there is a an onConnectionStateChange event indicating the new connection state is Disconnected - with (error) status 133.
Must still remember to call mBluetoothGatt.close() else an interface leak occurs and subsequent connection is made on the next client interface.
.
device.connectGatt() -- Peripheral still advertising but connection cancelled after 200ms using mBluetoothGatt.disconnect() and close().
Result: (I found this case the most interesting, if also the most unlikely to happen in a real application) Sometimes, (about 1 in 10)
the underlying Bluetooth Service actually did connect to the Peripheral; which saw a connect, followed by a disconnect; even though the App doesn't see
these events in the call back. Sometimes, even though, as far as the App is concerned, the App is disconnected, the underlying Bluetooth phone service
connected to the Peripheral - and remained connected in my test for a few minutes until I timed out! - and turned the BT service, or the Peripheral, off.
First, if an established connection is dropped you should get a disconnected state change event when the supervision timeout has passed. Otherwise there is some bug in Android.
Now about connection attempts.
Once you create a BluetoothGatt object with connectGatt and specify the auto connect parameter to true OR execute the connect method on an existing BluetoothGatt object, the phone will be in a state where it always and indefinitely tries to connect to the device and reconnect to the device if it disconnects for any reason until you either call disconnect or close on the gatt object.
So if you want to abort the connection after a while, just set up any kind of timer which calls disconnect on the gatt object (or close if you don't need it anymore) when it is triggered.
Also note that the status parameter of the onConnectionStateChange when newState is disconnected is not well-defined. In older Android versions it contains usually 0 or 133 and in newer versions often the Bluetooth standard's error code of the disconnect reason.
Also, if you get a disconnect state change event without previously have got a connected state change event, it usually indicates something has gone wrong in the internal Bluetooth stack (unless you use non-auto connect for which you always get a disconnect state change event after some timeout). Then I'd recommend that you close the gatt object and try again later.

How can i disconnect in SignalR as my Mobile Data Connections gets off

I had implemented ASP.NET SignalR with Android App. App works great but there is a problem. when i off the data connections, It takes time SignalR to disconnect the connection id created while onConnected() because it try to make connections till the time reach up to disconnection time it try to keep making connections, as a result the period of approx 1.5 min the app become useless. which i don't want. I want if a connection gets break due to loss of connection should be disconnected immediately. Has anyone a solution for that.
You should create your own method to stop the clients connection on such an event. You would probably use something similar to this
connection.hub.stop();
But if you really want the server to not wait you'll adjust the disconnect timeout, but if you do you'll have to adjust the keep-alive timeout accordingly if that was set manually.
DisconnectTimeout
www.asp.net/signalr/overview/guide-to-the-api/handling-connection-lifetime-events#disconnecttimeout
This setting represents the amount of time to wait after a transport connection is lost before raising the Disconnected event. The default value is 30 seconds. When you set DisconnectTimeout, KeepAlive is automatically set to 1/3 of the DisconnectTimeout value.
// Wait a maximum of 30 seconds after a transport connection is lost
// before raising the Disconnected event to terminate the SignalR connection.
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);

Why would turning android phone RF on/off cause a reboot?

So I have a need to force an android phone to register/de-register itself repeatedly. I'm accomplishing this via AT commands while the phone is in modem/diag mode over a USB serial connection. I check the current registration state via +CGREG and then assuming it's registered I kill the RF with +CFUN, check again, then turn the RF back on and allow the phone to search out and re-register itself normally. I issue the +CGREG command every few seconds to check on the status and once it's re-registered I repeat.
Now, sometimes I'll do this once or twice, sometimes it'll go five times and then the phone will decide to restart itself... it's a little frustrating and not the desired result.
Anyone have any ideas why the phone would reboot without command due to what I'm doing here? The phone will operate normally any number of hours while I'm not running these tests.
Psudo-Process:
while(true)
AT+CGREG? // starting state < usually registered>
AT+CFUN=4,0 // turn off RF
AT+CGREG? // verify it's not registered
AT+CFUN=1,0 // turn RF back on
while ( )
AT+CGREG? // check registration state every 2 seconds
end
end
On a side note: +COPS is a better option here, however the phone doesn't [fully] support it. The phone is an HTC Thunderbolt.

How to prevent the phone from losing IP traffic?

I have a simple app that periodically sends HTTP_GET requests to a server. When sending requests over 3G, I noticed that the requests sometimes time out (and the server-side logging shows that it NEVER receives the request either).
After trying out different combinations I found one consistant pattern when this problem occures (it times out after every 5-15 successful requests).
- TelephonyRegistry: notifyDataConnection() state=2isDataConnectivityPossible()true, reason=null
- TelephonyRegistry: broadcastDataConnectionStateChanged() state=CONNECTEDtypes=default supl, interfaceName=rmnet0
- NetworkLocationProvider: onDataConnectionStateChanged 3
According to Google, NetworkLocationProvider is changed to 'DATA_SUSPENDED', which implies "connection is up, but IP traffic is temporarily unavailable". (see TelephonyManager). On the situations where HTTP_GET requests succeeds, the state is changed to '8'. My app doesn't use the location manage and I've shut down every other non-critical app from running!
I want to know:
What is the cause of this issue? Why does the connection status go to DATA_SUSPENDED?
Is it possible to avoid/overcome this problem?
Any help/insight into this is much appreciated! Thanks in advance!
I have the same problem with my app running on an Huawei IDEOS X3 with Android 2.3.5. The app sends data each minute to a server using HttpClient.
Using logcat I can see that the data connection is lost and then reestablished after a short while. Previously my app stopped working since it tried to send data without a connection causing an exception which was not properly handed.
I don't know the reason for the intermittently dropped data connection but I now handle the situation by checking if there is a data connection prior to sending the data. In my case it does not matter if some data is never sent. If it was important to avoid data loss, I could buffer the data and send it once the connection was back.
public Boolean isDataConnection() {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
return tm.getDataState() == TelephonyManager.DATA_CONNECTED;
}

Bluetooth socket connection

How can i know if a BluetoothSocket is still connected to the endpoint? How can i detect if the socket has been disconnected by the endpoint?
Thanks
In my apps, I keep track of I/O errors. If a successful read() takes place, then I reset the counters. If the error counters go up high enough (4-5 is usually a good number) then I consider the connection dead, and proceed to tear it down and re-build it.
The SDK talks about a state change intent, but I'm not clear whether it's referring to a specific connection, or the bluetooth adapter itself here:
Optionally, your application can also
listen for the ACTION_STATE_CHANGED
broadcast Intent, which the system
will broadcast whenever the Bluetooth
state has changed. This broadcast
contains the extra fields EXTRA_STATE
and EXTRA_PREVIOUS_STATE, containing
the new and old Bluetooth states,
respectively. Possible values for
these extra fields are
STATE_TURNING_ON, STATE_ON,
STATE_TURNING_OFF, and STATE_OFF.
Listening for this broadcast can be
useful to detect changes made to the
Bluetooth state while your app is
running.

Categories

Resources