Android MulticastSocket closing implicitly - android

I seem to be having a small problem with MulticastSocket on Android: writing an SSDP-related application. The socket works just fine when I set everything up the first time, but when I stop discovery, and try to restart things, I just get a SocketException: Socket Closed. I'm not closing the socket, I'm simply stopping the Kotlin Coroutine that is responsible for calling socket.receive() in a loop. Example:
fun listenForPackets(): Flow<DatagramPacket> {
return flow {
multicastSocket.use {
val incomingBuffer = ByteArray(MULTICAST_DATAGRAM_SIZE)
while (true) {
val incomingPacket = DatagramPacket(incomingBuffer, incomingBuffer.size)
it.receive(incomingPacket)
emit(incomingPacket)
incomingPacket.length = incomingBuffer.size
}
}
}
}
The problem
So the problem is that when I try to call that function again, I get a SocketException: Socket Closed. The socket initialization code is run once, meaning that toggling discovery on/off will use the same socket multiple times; the following code is run once throughout the whole application:
multicastSocket = MulticastSocket(MULTICAST_PORT)
multicastSocket.reuseAddress = true
multicastSocket.joinGroup(multicastGroup)
multicastLock.acquire()
What I have tried
My first thought was that I was not cancelling the Kotlin Coroutine correctly. As a result, I switched to using typical Java Threads, to no avail. Starting the thread the first time works, but, restarting discovery yields the same problem. I have also tried to not leave the group, and keep the multicastLock acquired - same problem.
What works
What works is having the initialization code (where I assign the socket, join the group, and acquire lock) run every time I need to start a scan. At the end of the scan, I reset all of the variables (leave group, release lock, close socket). So my question becomes - is this the correct approach? Or am I simply doing something else wrong?
Just to re-iterate, I'm discovering packets just fine, the issue is with restarting the discovery. Thank you in advance for any help!

Related

Questions on creating a service that connects automatically to a BLE device on android

I am implementing a service that uses the autoconnect feature of bluetoothGatt to connect to the device and monitor it while it is being connected.
I work on the assumption that the device is already bonded (a coworker is responsible for that part) so autoconnect should not have any problems
my code is as follows:
//the callback is for the class I have created that actually does the connection
class BTService: Service(), CoroutineScope, BTConnection.Callback {
private val btReceiver by lazy { BluetoothStateReceiver(this::btStateChange) } //receiver for bt adapter changes
private var connection:BTConnection? = null
private var readJob:Job? = null
override fun onCreate() {
buildNotificationChannels()
registerReceiver(btReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)) //since I can't register this receiver in AndroidManifest any more I did it here
}
private fun btStateChange(enabled: Boolean) {
if (enabled)
startConnecting()
else
stopConnection()
}
private fun startConnecting() {
val address = prefs.address //get the current saved address
val current = connection //get the current connection
//try to stop the current connection if it is different than the one we want to set up
if (current != null && !current.address.equals(address, true))
current.stop()
if (address.isNullOrBlank())
return
//then we create a new connection if needed
val new = if (current == null || !current.address.equals(address, true)) {
Injections.buildConnection(application, address, this)
} else {
current
}
connection = new
new.connect()
}
//this is one of the callbacks from BTConnection.Callback
override fun connected(address: String) {
if (address != connection?.address) return
val cn = connection ?: return
showConnectionNotification()
val notification = buildForegroundNotification()
startForeground(FOREGROUND_ID, notification)
readJob?.cancel()
readJob = launch {
cn.dataFlow //this is a flow that will be emmitting read data
.cancellable()
.flowOn(Dispatchers.IO)
.buffer()
.onEach(this#BTService::parseData)
.flowOn(Dispatchers.Default)
}
}
private suspend fun parseData(bytes:ByteArray) { //this is where the parsing and storage etc happens
}
private fun stopConnection() {
val cn = connection
connection = null
cn?.stop()
}
override fun disconnected(address: String) { //another callback from the connection class
showDisconnectNotification()
stopForeground(true)
}
my code that stops the connection is
fun stop() {
canceled = true
if (connected)
gatt?.disconnect()
launch(Dispatchers.IO) {
delay(1000)
gatt?.close()
gatt = null
}
}
my code is based (and affected) by this really good article I read:
https://medium.com/#martijn.van.welie/making-android-ble-work-part-2-47a3cdaade07
I have also created a receiver for boot events that will call
context.startService(Intent(context, BTService::class.java))
just to make sure that the service is created at least once and the bt receiver is registered
my questions are:
a) is there a chance that my service will be destroyed while it is not in foreground mode? i.e. when the device is not near by and bluetoothGat.connect is suspending while autoconnecting? is it enough for me to return START_STICKY from onStartCommand() to make sure that even when my service is destroyed it will start again?
b) if there is such a case, is there a way to at least recreate the service so the btReceiver is at least registered?
c) when should close() be called on bluetoothGatt in case of autoconnect = true? only when creating a new connection (in my example where I call Injections.buildConnection)? do I also call it when the bluetoothadapter is disabled? or can I reuse the same connection and bluetoothGatt if the user turns the bluetooth adapter off and on again?
d) is there a way to find out if autoconnect has failed and will not try again? and is there a way to actually test and reproduce such an effect? the article mentioned above says it can happen when the batteries of the peripheral are almost empty, or when you are on the edge of the Bluetooth range
thanks in advance for any help you can provide
a-b) If your app does not have an activity or a service that is in the foreground, the system may kill it at anytime. Pending or active BLE connections doesn't affect the system's point of view when to kill the app whatsoever. (When it comes to scanning for advertisements, the story is completely different though.)
The general approach to make sure autoConnects stay alive is to have a foreground service running at all the time. So don't stop it while the device is currently not connected, if you want to have a pending connection. There is no point in using Job Scheduler, WorkManagers etc. since having a foreground service should be enough to keep the app process alive, and pending/active connections are kept alive as long as the app is. The app does not use any cpu% at all when waiting for pending BLE connections. However some Chinese phone makers are known to not follow the Android documentation, by sometimes killing apps even though they have running foreground services.
c) Each BluetoothGatt object represents and refers to an object inside the Bluetooth process running on the same phone. By default the system allows a total of 32 such objects (last time I checked). In order to release these precious resources, you call close(). If you forget, you will have a leak, meaning your app or some other app might not be able to create a BluetoothGatt object. (When app processes exit, their BluetoothGatt objects are however closed automatically). The API is a bit strangely designed, that there is both a disconnect method and a close method. But anyway, the disconnect method gracefully initiates a disconnection of the connection and you will then get an onConnectionStateChange callback telling when the disconnection is complete. You must however call close in order to free the resource, or call connect if you'd like to re-connect, or you can take an action a bit later. Calling close on a connected BluetoothGatt object will also disconnect, but you won't get any callback due to the object is being destroyed at the same time.
Since all BluetoothGatt objects represents objects in the Bluetooth process, these will "die" or stop working when you turn off Bluetooth, since that involves shutting down the Bluetooth process. This means you need to recreate all BluetoothGatt objects when Bluetooth is restarted. You can call close on the old objects, but it won't do anything since they're dead. Since the documentation doesn't say anything about this, I suggest you call close anyway to be on the safe side if the behaviour is changed in the future.
d) To detect if a connectGatt call fails and will not try again, you can listen to the onConnectionStateChange callback. If this gives an error code, such as 257, it usually means that the system has reached maximum number of connections, or maximum number of some resource. You can test this out by simply initiating pending connections to a bunch of different Bluetooth device addresses.
I would not trust the statement that new connection attempts would be aborted if the peripheral is low on battery or being on the "edge of Bluetooth range". I'd be glad to see a pin point to Android's Bluetooth source code where this happens, since I really believe this is not true at all.
First of all, if you are intending to distribute your app to Google Play Store, you need to be targeting minimum api level 29 if I'm not mistaken, hence you should be using either JobService along with JobScheduler or WorkManager, instead of Service. This is to support the background limitations from Oreo(26) onwards.
a) if you properly implement any of the two options I mentioned above, you can write a proper service that will not terminate unless you stop it. Here are some resources on JobService : (resource1, resource2, resource3)
b) You can re-register as you please upon the onStartJob() method of your JobService, which will recreate your app.
c) Each time you are done with the peripheral ble device, you need to close the gatt connection with it. Here is a snippet from the BluetoothGatt class
/**
* Close this Bluetooth GATT client.
*
* Application should call this method as early as possible after it is done with
* this GATT client.
*/
public void close() {
Also, from the BluetoothAdapter class javadoc, you can see that all the connections are terminated gracefully when ble is disabled.
/**
* Turn off the local Bluetooth adapter—do not use without explicit
* user action to turn off Bluetooth.
* <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
* system services, and powers down the underlying Bluetooth hardware.
* <p class="caution"><strong>Bluetooth should never be disabled without
* direct user consent</strong>. The {#link #disable()} method is
* provided only for applications that include a user interface for changing
* system settings, such as a "power manager" app.</p>
* <p>This is an asynchronous call: it will return immediately, and
* clients should listen for {#link #ACTION_STATE_CHANGED}
* to be notified of subsequent adapter state changes. If this call returns
* true, then the adapter state will immediately transition from {#link
* #STATE_ON} to {#link #STATE_TURNING_OFF}, and some time
* later transition to either {#link #STATE_OFF} or {#link
* #STATE_ON}. If this call returns false then there was an
* immediate problem that will prevent the adapter from being turned off -
* such as the adapter already being turned off.
*
* #return true to indicate adapter shutdown has begun, or false on immediate error
*/
#RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean disable() {
d) I am not sure on what callback will be triggered. To reproduce, the two items you mentioned seem like valid cases to try.
I hope this helps you perfect your project!

Native network-related functions randomly blocking in android

I have a library written in C that I'm using from an Android application. I discovered that the application blocks sometimes while executing various functions (what is common is they are all related to networking).
I'm testing on a real Android device running Android 6.
The library is compiled with NDK version 21.
So far I have observed this behavior with 3 functions - getaddrinfo, poll and socket:
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
getaddrinfo(hostname, port, &hints, &result);
////
struct pollfd wait = {
.fd = fd,
.events = event,
.revents = 0
};
status = poll(&wait, 1, 10000);
////
fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
These functions are called several times each after the program starts running. The same call can work fine one time and block indefinitely (at least several minutes) the next time. The internet connection on the device seems to be fine.
Any ideas? How can I even approach debugging such a problem?
I didn't actually find the reason for the problem, but I found how to fix it. Part of the code was written by somebody else and I wasn't sure what exactly it was doing. After rewriting this part, everything started to work fine.
Among other things, the code was spawning a new process with fork(). I suspect the problem to be related to that.

Android - Wifimanager handle wifi-connection states

I've got an app which connect itself programatically to a wifi connection. My problem is, I want to handle the case, that the password is wrong. I want to detect that the password is not correct in runtime. To be precise I've got a progressdialog running while the connection is established, so if the password is wrong the progressdialog is just shown all the time and can't be skipped. A further note: I handled a password which is less than 8 characters by using this code:
if(!m_wifiManager.enableNetwork(netId, true)) {
progressDialogConnecting.dismiss();
createInfoMessageDialog(CONST.WIFI_CON_FAILED_TITLE, CONST.WIFI_CON_FAILED_MSG_CONFAILURE);
m_wifiManager.reconnect();
return;
}
If the key for the wifi connection is less than 8 characters, this if-case gets triggered. But if it is longer than 8 characters and wrong I get an endless state of showing the progress dialog.
What I exactly want to ask: how do I handle 1. wrong password 2. connection states (just like Android system showing me the toasts "Connected to Wifi xyz") ? AND is it even possible to handel the first one (wrong password)?
Here is the code, that did not work for handling connection established event (this is just the wifirecevier, I also registered it in the activity):
public class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
if (intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)){
if(wrongNetworkConnected)
progressDialogConnecting.dismiss();
}
}
} else {
}
}
}
}
Edit: What I am currently doing, is that I have a Handler which tells me to whom I am connected. That's useful because I can say that after the reconnect() I am reconnected to the old network (current network) and not the new one - so apparently the password could be wrong (or something else), because I could not connect to the new network.
The problem about this method is that first of all it takes too much time and secondly it is not reliable. I can lie and say that if you will get reconnected to your current network it is the fault of a wrong password, but actually it is not 100% sure that you cannot reconnect because of this - it may also have other reasons. So I am still searching for a simple feedback/handle from the suplicant that the password is wrong, just like the android api does in the wifi settings of each android device...
My problem is, I want to handle the case, that the password is wrong.
After some research I found this post which is not marked as answered but it still worked for me very well.
Here is the if-case in which the program jumps (already tested several times by me) if there is an authentication error --> e.g. wrong password:
int supl_error=intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
if(supl_error==WifiManager.ERROR_AUTHENTICATING){
// DO SOMETHING
}
NOTE: As seen in the linked post above this if-case should appear in a BroadcastReceiver adding the intent WifiManager.SUPPLICANT_STATE_CHANGED_ACTIONto the receiver-registration in your activity-class.

App using Mobile Android GNSK crashes when identifyAlbumAsync() is called before audioProcessStart()

I have being upgrading an application to use the new Mobile Android GNSK but I have noticed that using the new MusicID-Stream is a little bit tricky. If the "identifyAlbumAsync" method get executed before the "audioProcessStart" method(since this need to be executed in a different thread), the application just crashes. In the Gracenote Demo application, the "audioProcessStart" method is continuously running so there is no need to synchronize its execution with the "identifyAlbumAsync" method call. Is it the way it is supposed to be used? It will be convenient if the application didn't crashed at least when the methods are not executed in order. Also in our application, we don't want to have the "audioProcessStart" method continuously like it is done in the demo application. We only want to run the "audioProcessStart" method when the user request identification and when the song playing gets identified , we want to stop the audio processing by calling "audioProcessStop". Is there an easy way to do this? Right now, we are getting the Thread where "identifyAlbumAsync" is running to sleep for 2 seconds in order to make sure that the Thread where the "audioProcessStart" method is supposed to run has time to get executed. Thank you in advance for your prompt response
In the upcoming 1.2 release, IGnMusicIdStreamEvents includes a callback that signals audio-processing has started, and an ID can be synced with this, e.g.:
#Override
public void musicIdStreamProcessingStatusEvent( GnMusicIdStreamProcessingStatus status, IGnCancellable canceller ) {
if (GnMusicIdStreamProcessingStatus.kStatusProcessingAudioStarted.compareTo(status) == 0) {
try {
gnMusicIdStream.identifyAlbumAsync();
} catch (GnException e) { }
}
}
Thanks for the feedback, you're right about this issue. Unfortunately right now sleeping is the best solution. But we are adding support for an explicit sync event in an upcoming release, please stay tuned.

How do I open up a ServerSocket in my android application?

Ok so I am doing something that seems like it should be very easy. I am basically trying to open up a ServerSocket connection and then wait for the client to connect.
Here is my code.
ServerSocket serverSocket = new ServerSocket(6543);
Socket clientSocket = serverSocket.accept();
Whenever my code hits, serverSocket.accept();, I am throwing the following exception.
bind failed: EADDRINUSE (Address already in use)
So obviously my next step was to check and see if another port would work, it did not. Next I restarted the device and tried running the app and I got the same exception. I have given my app INTERNET permission and the device is rooted.
Here are my network interfaces that show up.
lo: ::1%1
lo: 127.0.0.1
eth0: //IPV6 address
eth0: 192.168.1.127
EDIT 1
Here is the serverSocket object info that I get when debugging.
ServerSocket[addr=192.168.1.121/192.168.1.121,port=0,localport=1234]
EDIT 2
I have the following available constructors in my ServerSocket
new ServerSocket(int port)
new ServerSocket(int port, int backlog)
new ServerSocket(int port, int backlog, InetAddress localAddress)
I tried using the 3rd constructor and same exception.
new ServerSocket(4567, 0, InetAddress.getLocalHost());
Alright so I finally figured out why my application was throwing an exception and if anyone has some feedback on this or would like to provide an answer as to why this was happening. I would greatly appreciate it.
So in my Thread that I created I need to postback to the main thread to update some TextView's based upon what was happening with the server connection. Well since I can't update the main thread from my self created thread I needed to add a Handler object to that Thread in order to update the UI. Removing that Handler completely allowed my application to successfully create the ServerSocket without throwing an exception.
Does the Handler somehow run the code before hand or what was happening here?
The code.
RemoteServerRunnable test = new RemoteServerRunnable();
Handler handler = new Handler();
handler.post(test);
test.start();

Categories

Resources