How to force MQTT broker to NOT clean session from Android Paho client? - android

I'm trying to use MQTT (Paho library on Android, mosquitto message broker on a linux server) to pass moves in a turn-based game, replacing a custom server I wrote years ago. Its simplicity and pub-sub design seem perfect: each device subscribes to a unique id as "topic" and communicates that as its "address." Then other devices can reach it by publishing to that address.
It works perfectly in my test Linux client (connecting using the mosquitto-dev library on Ubuntu). And it works perfectly on Android WHEN THE ANDROID APP IS RUNNING. In the Linux client case, if a message is sent while the app isn't running or connected the app receives the message as soon as it does connect and subscribe. On Android, however, this doesn't happen. Only messages sent (or resent) by another client while the android client is subscribed are ever delivered.
I'm new to MQTT, but it seems pretty clear that the "cleanSession" connection parameter is what controls this: unless you "clean" a session, you get everything that was published to your topic while you were not subscribed. On the Linux client side, passing "true" to mosquitto_new(..., clean_session, ...) does indeed prevent my Linux client from getting pre-connection messages. But on the Android side, calling .setCleanSession(boolean) has no effect when the MqttConnectOptions instance is passed to .connect().
I'm using 1.1.+ of paho. Per the tags in the repo at https://github.com/eclipse/paho.mqtt.android.git, v1.1.1 is the latest.
implementation "org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.+"
implementation "org.eclipse.paho:org.eclipse.paho.android.service:1.1.+"
I suspect that this is simply a bug in the Android Paho library (which doesn't seem to have been worked on in four years.) But I hope I'm wrong! Is there a way to accomplish what I want?
Alternatively, is there a better library? The googling I've done suggests that in spite of its age Paho is still what most Android devs are using to speak MQTT.
Thanks!

About the cleanSession flag.
The Client and Server can store Session state to enable reliable messaging to continue across a sequence of Network Connections. This bit is used to control the lifetime of the Session state.
If CleanSession is set to 0, the Server MUST resume communications with the Client based on state from the current Session (as identified by the Client identifier). If there is no Session associated with the Client identifier the Server MUST create a new Session. The Client and Server MUST store the Session after the Client and Server are disconnected. After the disconnection of a Session that had CleanSession set to 0, the Server MUST store further QoS 1 and QoS 2 messages that match any subscriptions that the client had at the time of disconnection as part of the Session state. It MAY also store QoS 0 messages that meet the same criteria.
More about cleanSession on : https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/csprd02/mqtt-v3.1.1-csprd02.html
If I understand correctly your requirement, then indeed you need to use clenSession=true. You can also try publishing and subscribing with QoS=0. Some brokers do not store QoS=0 messages, mosquitto as well. (as per https://mosquitto.org/man/mqtt-7.html)

I've found a workaround, and in the process confirmed my suspicion that the MqttAndroidClient class is broken in not honoring that setting. Instead of using MqttConnectOptions I tried using MqttAsyncClient. The code changes are trivial, though underneath there's considerable change as the Android client knows about and uses background Services. With the simple change of using this different class, I'm able to connect & subscribe and immediately receive all messages that were published while I was not connected.

Related

How to automatically assign a client certificate in https RESTRequest communication on Android

I'm using the TRESTClient, TRESTRequest, TRESTResponse component stack for communication with the https REST API service. The Server authenticates the client by sending the request using the client certificate.
In my Android application, after calling the RestRequest.Execute (rmPost) method, a form appears asking for the selection of the client certificate. I would like to automatically assign a client certificate and avoid this additional question, but the RESTClient.OnNeedClientCertificate event is not fired.
I checked RESTLibrary, and I separately checked TNetHttpClient (the event OnNeedClientCertificate is also not fired), after several hours of reading blogs and performing dozen of tests, I am so frustrated as I have no idea how to solve the problem.
How can I avoid this additional question about choosing a client certificate and set the client certificate automatically?
Why is the TRESTClient.OnNeedClientCertificate event is not fired?
Thank you for any suggestions and help.

Custom IoT Endpoint

We need to use a custom IoT endpoint due to firewall restrictions and needing to utilize Static Ips. We followed this AWS doc to get our endpoint with static Ips.. From here we are attempting to call the CreateKeysAndCertificate via Java. Now when we call IoT with our custom domain name, iot.custom.domain.name.com, with the regular Java SDK it works fine. However, whenever we try to use the Android SDK and call setEndpoint with our custom domain we get the following error
com.amazonaws.services.iot.model.ResourceNotFoundException: Not Found (Service: AWSIot; Status Code: 404; Error Code: ResourceNotFoundException
Any help or guidance on this would be appreciated.
When using the Android SDK for establishing IOT connections, the CreateKeysAndCertificateRequest API is available through the AWSIotClient class. If you are using the AWSIotClient for creating new certs/keys, the SDK places this request on the generic iot.<region>.amazonaws.com endpoint. The setEndpoint method just allows you to change the region. This is because the request goes to the Control plane, whereas the endpoint that you have created would mostly likely be on the Data plane. There is no way around to create new certs/keys using the AWSIotClient on the custom endpoint.
There is an alternate option that you can make use of. Almost all "requests" that you place on the IOT endpoint are messages that are published to "reserved topics". If you open up the Java SDK's PublishCreateKeysAndCertificate API, you will see that it is ultimately publishing a message over a reserved topic. You can do something similar on Android using the Android SDK as well.
First, you will have to establish an authenticated connection. We cannot use CognitoCredentialsProvider because of that auth request going to the Control Plane. Instead, you can use the provisioning certificates for the first time authentication. This is through provision certificates generated for a Provisioning Fleet. You can create a Provisioning Fleet and use those certificates in your device's keystore (or, a PKCS12 cert file). Using that, you can create a new awsIotMqttManager object and publish a message on the reserved topic meant for creating new certs/keys. You can also subscribe to reserved topics meant for receiving the "accepted"/"rejected" responses for this request.
TL;DR
Create an awsIotMqttManager using the provision certs
Subscribe to topic for listening for accepted/rejected response for CreateKeysAndCertificates request
Publish a message over the reserved topic meant for CreateKeysAndCertificates
Register the thing using the ownershipToken received in the response
Store the new certs and use them for all future connections (make sure the policy attached to the certs have the necessary permissions)

Google Cloud socket.io server, client not keeping persistent connection

I have a Node.js server using socket.io to connect Android apps and I've been hosting it locally by just running it in my IDE and connecting to my local IPv4 address but I want it to work without me having to keep my PC running constantly so I've tried using Google Cloud and managed to get it mostly working but the client doesn't keep the connection and disconnects consistently.
I followed this tutorial up to step 4 after that I ran gcloud app deploy.
My Node.js server is in one file, it has these declarations at the top.
const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
I then have this for the initial client connection.
io.on("connection", (socket) => {
console.log("User connected.");
Everything inside of this is just listeners for what gets emitted by the client so I don't think they're the problem.
And then outside of that I have
server.listen(8080, () => {
console.log("Server is listening");
});
I don't know if anything from the package.json file is relevant but I can provide it if need be.
After deploying to Google Cloud using the tutorial there are a few things in the logs that may be the reason behind the problem.
Waiting for network connection open. Subject:"app/invalid" Address:127.0.0.1:8080
Waiting for network connection open. Subject:"app/invalid" Address:127.0.0.1:8081
Wait successful. Subject:"app/invalid" Address:127.0.0.1:8080 Attempts:97 Elapsed:485.916418ms
App is listening on port 8080. We recommend your app listen on the port defined by the PORT environment variable to take advantage of an NGINX layer on port 8080.
These might have simple solutions but I have very little experience with server hosting.
Once my Android client connects to the server, the log outputs "User connected." as it should, then around 10 seconds later it does it again and this repeats. There's no error I can see between the connections just a few socket.io POST/GET requests.
I tried adding session affinity to the app.yaml but hasn't solved it either.
This is my app.yaml if any changes need to be made here
runtime: nodejs16
env: standard
instance_class: F1
automatic_scaling:
min_idle_instances: automatic
max_idle_instances: automatic
min_pending_latency: automatic
max_pending_latency: automatic
network:
session_affinity: true
Thanks for any help, if I need to provide any other files I can do so.
Socket communication requires persistent network connections. So, App Engine Flexible provides an option to keep the network connection alive.
I think you are deploying your app in App Engine Standard , which does not support this option.
So, you can deploy your app in App Engine Flexible and you need to include the following configuration in your app.yaml
network:
session_affinity: true
For refence on session affinity, please refer to this page https://cloud.google.com/appengine/docs/flexible/nodejs/using-websockets-and-session-affinity#session_affinity
So, your updated app.yaml can look like this
runtime: nodejs
env: flex
network:
session_affinity: true
Please refer to this page for supported yaml configuration for flexible environment - https://cloud.google.com/appengine/docs/flexible/nodejs/reference/app-yaml
Messages you have pasted are most likely not indicating a problem. Your application is running in a sandbox environment for which a container instance and a web server must be initialized the first time that your app engine service receives a request. This is also called Loading request and the messages you see indicate the startup of your container instance and your webserver.
You can take a look at Google's own documentation regarding handling requests in node.js or follow a quickstart guide.
If there are no other logs during the disconnection, I would suggest checking the quotas to see if you're not exceeding any.

How to implement Kurento Client JS with your own "Tomcat signalling server" on Android using a secure SSL connection to KMS?

So this is a two part question:
Part a: I'm trying to implement a secure connection to the KMS. From the documentation, I've understood that KMS Configuration file would need to be updated with the SSL certificate and then the HTTPS connection from the client can be made. Please let me know if there are any other steps that are involved in achieving SSL security.
Part b: From a better understanding now and from comments from a previous question I posted, Kurento Utils does not connect to KMS directly (this was an fyi and a clarification I received and I wanted documented here just in case). Now I'm trying to use Kurento Client to connect to KMS and I'm trying to understand the role of ICE/TURN/STUN servers acting as negotiators in the middle. If I were to specify my own server URL, I'm assuming that I would not need to include "freeice" and "normalice" and instead specify my own server's URL. In the code snippet below taken from the tutorial on github, I'm assuming that I would need to replace the argument for ice_servers to point to the url where my server is running? Or since this is the client, do I really need an ICE server because as said from the first statement, the utils don't connect to the KMS but the client can, right? So if I were to specify the Kurento URL for "ws_uri" parameter, then I won't need to even use ICE servers...right? I don't really understand the concept of ICE/TURN servers very well in terms of how they integrate with Kurento and hence, I would like to understand in English as to what changes would I need to make in order to get this to work. I will bang my head to write the code myself! Thanks much in advance!
`
var args = getopts(location.search,
{
default:
{
ws_uri: 'ws://' + location.hostname + ':8888/kurento',
file_uri: 'file:///tmp/recorder_demo.webm', //file to be stored in media server
ice_servers: undefined
}
});`
Answer A
Only this and nothing more... at least for KMS. On the client side, you'll need to specify the WSS port and so on.
Answer B
Your client might need a STUN/TURN server, and that's independent of where KMS is located. STUN and TURN are used in the candidate harvest process, to discover the network topology of your peer. You have two peers: KMS and your Android app, and both need to have, in their SDPs and during the negotiation, a candidate that is reachable by them (app will connect with KMS and viceversa) If both peers are on the same network, you can go without using STUN/TURN. The moment you have a NAT in between, you need at least STUN for that peer to be able to harvest candidates that have the public IP on the other side of the NAT, which is not known by the peer unless STUN is used.
TURN is used as a relay server, and it is needed in a small set of cases. If you are almost certain you are going to use TURN, you need to have that in a machine different than KMS (it makes close to no sense to have both the relay server and the media server installed together)
So the answer is yes, you are most likely going to need STUN/TURN in your clients.

flexible offline messages retrieval returning false

In my XMPP chat application, I am using ASMACK library 4.0.6 on the Android client side.
I want to retrieve offline messages when an XMPP connection is established. For that, first I check whether the server has support for flexible retrieval. The server always returns false. We enabled the offline module in MongooseIM server. But why am I getting false from server?
if (!offlineMessageManager.supportsFlexibleRetrieval()) {
Log.i("Offline messages not supported","" + offlineMessageManager.supportsFlexibleRetrieval());
return;
}
If I try with pidgin client, I get offline messages..
Disclaimer: I work on MongooseIM.
Guessing from the API you're trying to use it implements XEP-0013 - MongooseIM doesn't support this XEP. Ensure what protocol .supportsFlexibleRetrieval() really uses underneath.
MongooseIM supports XEP-0313 version 0.2 and will support the newest version of this XEP. Verify whether your client library supports this XEP and which version of it. The relevant module to run on the server is mod_mam. Please refer to the MongooseIM wiki on GitHub, since its configuration is a bit complex.
Alternatively, you can rely on mod_offline automatically pushing the offline messages when a resource connects.

Categories

Resources