Moto G5 Plus can't connect via Socket to a local server - android

I have an application where the smartphone must connect via SSLSocket to a local server. I tested my app on 5 different smartphones: Moto G2 (6.0), Redmi 3S (6.0.1), LG K5 (6.0), Moto G5 Plus (7.1.1) and OnePlus 5 (8.0). The Moto G5 Plus was the only one to show this problem.
This is the line that causes the problematic behaviour. All tests where done on the same network.
socket = (SSLSocket) sslContext.getSocketFactory().createSocket(serverAddress, serverPort);
Is there any known problem with Moto G5 Plus or with Android 7+ around this behaviour?
EDIT: Some more tests are leading to an idea of Android system trying to force the Socket to connect through mobile network when identifying that WiFi interface is connected, but with no internet. Is there any way to enforce the Socket to use WiFi instead of mobile network?

Disclaimer: I didn't test this so I'm really not sure if it works.
The Network class has a bind(Socket) method, maybe you could find the wifi network and then bind it to your socket. From the doc it seems that this is what you need, it says:
/**
* Binds the specified {#link Socket} to this {#code Network}. All data traffic on the socket
* will be sent on this {#code Network}, irrespective of any process-wide network binding set by
* {#link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
*/
The Socket shouldn't be connected before binding to the network, so I think you should create it with socketFactory.createSocket() and connect it only after the binding.
So, you should first find your Network (Kotlin):
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val wifiNetwork = connectivityManager.allNetworks.firstOrNull {
val info = connectivityManager.getNetworkInfo(it)
info.type == ConnectivityManager.TYPE_WIFI
}
or (Java)
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
Network wifiNetwork = null;
for(Network network : connectivityManager.getAllNetworks()){
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI){
wifiNetwork = network;
break;
}
}
Then bind it to the Socket and finally connect (Kotlin):
wifiNetwork?.bindSocket(socket)
val socketAddress = InetSocketAddress(hostname, port)
socket.connect(socketAddress)
or (Java)
if(wifiNetwork != null){
wifiNetwork.bindSocket(socket);
}
InetSocketAddress socketAddress = InetSocketAddress(hostName, port);
socket.connect(socketAddress);
Note, it needs ACCESS_NETWORK_STATE permission
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

i hope it helps you,i just found your solution on github.
for further details and for official link,please check this out, i think this can be helpful to you.
if it is not the answer,please ignore this answer.
We are using AsyncTask to avoid StrictMode fatal error for network access ( Look in references ). The StrictMode policy is simply forbidding us to affect on UI Thread.
/* AsyncTask class which manages connection with server app and is sending shutdown command.
*/
public class ShutdownAsyncTask extends AsyncTask<String, String, TCPClient> {
private static final String COMMAND = "shutdown -s" ;
private TCPClient tcpClient ;
private Handler mHandler ;
private static final String TAG = "ShutdownAsyncTask";
/**
* ShutdownAsyncTask constructor with handler passed as argument. The UI is updated via handler.
* In doInBackground(...) method, the handler is passed to TCPClient object.
* #param mHandler Handler object that is retrieved from MainActivity class and passed to TCPClient
* class for sending messages and updating UI.
*/
public ShutdownAsyncTask(Handler mHandler){
this.mHandler = mHandler;
}
/**
* Overriden method from AsyncTask class. There the TCPClient object is created.
* #param params From MainActivity class empty string is passed.
* #return TCPClient object for closing it in onPostExecute method.
*/
#Override
protected TCPClient doInBackground(String... params) {
Log.d(TAG, "In do in background");
try{
tcpClient = new TCPClient(mHandler,
COMMAND,
"192.168.1.1",
new TCPClient.MessageCallback() {
#Override
public void callbackMessageReceiver(String message) {
publishProgress(message);
}
});
}catch (NullPointerException e){
Log.d(TAG, "Caught null pointer exception");
e.printStackTrace();
}
tcpClient.run();
return null;
}
In this AsyncTask we are creating TCPClient object ( explained below ). In the TCPClient constructor we are passing Handler object for changing the UI, COMMAND - the String with the "shutdown -s" command for shutting down the computer, IP number - the servers ip number; Callback object - when we are getting servers response, the callback method 'messageCallbackReceiver' is starting 'publishProgress' method, which is publishing progress to 'onProgressUpdate' AsyncTask's method.
/**
* Overriden method from AsyncTask class. Here we're checking if server answered properly.
* #param values If "restart" message came, the client is stopped and computer should be restarted.
* Otherwise "wrong" message is sent and 'Error' message is shown in UI.
*/
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
Log.d(TAG, "In progress update, values: " + values.toString());
if(values[0].equals("shutdown")){
tcpClient.sendMessage(COMMAND);
tcpClient.stopClient();
mHandler.sendEmptyMessageDelayed(MainActivity.SHUTDOWN, 2000);
}else{
tcpClient.sendMessage("wrong");
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
tcpClient.stopClient();
}
}
After receiving proper message, we are sending command, or if we received wrong message, we are sending message "wrong" and stopping client. After this we are being transferred to the 'onPostExecute' method:
#Override
protected void onPostExecute(TCPClient result){
super.onPostExecute(result);
Log.d(TAG, "In on post execute");
if(result != null && result.isRunning()){
result.stopClient();
}
mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 4000);
}
}
So step by step:
->AsyncTask is creating TCPClient object.
->In TCPClient constructor we are passing Handler, Command, IP Number and Callback object.
->When TCPClient begins connection, it sends message "shutdown" to the server.
->When we are receiving message from server, the callback is passing it to 'onProgressUpdate'.
->If the received message ( response from server ) is equal to "shutdown", we are sending COMMAND to server.
->After sending it we are stopping client, which is transferring us to 'onPostExecute' method.
->Meanwhile, the handler is receiving empty messages with 'msg.what' integers defined in MainActivity, which are responsible for updating the GUI.
Example of how the widget UI is updated:
mHandler = new Handler(){
public void handleMessage(Message msg) {
switch(msg.what){
case SHUTDOWN:
Log.d(mTag, "In Handler's shutdown");
views = new RemoteViews(context.getPackageName(), R.layout.activity_main);
widget = new ComponentName(context, MainActivity.class);
awManager = AppWidgetManager.getInstance(context);
views.setTextViewText(R.id.state, "Shutting PC...");
awManager.updateAppWidget(widget,views);
break;
TCPClient
This class is responsible for maintaining the connection. I will explain it step by step:
In the first step we can see the objects passed from ShutdownAsyncTask and others. Additionally we can see the sendMessage and stopClient methods.
public class TCPClient {
private static final String TAG = "TCPClient" ;
private final Handler mHandler ;
private String ipNumber, incomingMessage, command;
BufferedReader in ;
PrintWriter out ;
private MessageCallback listener = null ;
private boolean mRun = false ;
/**
* TCPClient class constructor, which is created in AsyncTasks after the button click.
* #param mHandler Handler passed as an argument for updating the UI with sent messages
* #param command Command passed as an argument, e.g. "shutdown -r" for restarting computer
* #param ipNumber String retrieved from IpGetter class that is looking for ip number.
* #param listener Callback interface object
*/
public TCPClient(Handler mHandler, String command, String ipNumber, MessageCallback listener) {
this.listener = listener;
this.ipNumber = ipNumber;
this.command = command ;
this.mHandler = mHandler;
}
/**
* Public method for sending the message via OutputStream object.
* #param message Message passed as an argument and sent via OutputStream object.
*/
public void sendMessage(String message){
if (out != null && !out.checkError()) {
out.println(message);
out.flush();
mHandler.sendEmptyMessageDelayed(MainActivity.SENDING, 1000);
Log.d(TAG, "Sent Message: " + message);
}
}
/**
* Public method for stopping the TCPClient object ( and finalizing it after that ) from AsyncTask
*/
public void stopClient(){
Log.d(TAG, "Client stopped!");
mRun = false;
}
The magic happens here - in 'run()' method. Here we are using 'try-catch' tools for handling exceptions ( server not enabled, ip not proper etc. ). As you can see below, we have infinite while() loop for listening to the incoming messages. We can simply stop it and finalize with 'stopClient()' method ( used in ShutdownAsyncTask's 'onProgressUpdate' method)
public void run() {
mRun = true;
try {
// Creating InetAddress object from ipNumber passed via constructor from IpGetter class.
InetAddress serverAddress = InetAddress.getByName(ipNumber);
Log.d(TAG, "Connecting...");
/**
* Sending empty message with static int value from MainActivity
* to update UI ( 'Connecting...' ).
*
* #see com.example.turnmeoff.MainActivity.CONNECTING
*/
mHandler.sendEmptyMessageDelayed(MainActivity.CONNECTING,1000);
/**
* Here the socket is created with hardcoded port.
* Also the port is given in IpGetter class.
*
* #see com.example.turnmeoff.IpGetter
*/
Socket socket = new Socket(serverAddress, 4444);
try {
// Create PrintWriter object for sending messages to server.
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//Create BufferedReader object for receiving messages from server.
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Log.d(TAG, "In/Out created");
//Sending message with command specified by AsyncTask
this.sendMessage(command);
//
mHandler.sendEmptyMessageDelayed(MainActivity.SENDING,2000);
//Listen for the incoming messages while mRun = true
while (mRun) {
incomingMessage = in.readLine();
if (incomingMessage != null && listener != null) {
/**
* Incoming message is passed to MessageCallback object.
* Next it is retrieved by AsyncTask and passed to onPublishProgress method.
*
*/
listener.callbackMessageReceiver(incomingMessage);
}
incomingMessage = null;
}
Log.d(TAG, "Received Message: " +incomingMessage);
} catch (Exception e) {
Log.d(TAG, "Error", e);
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
} finally {
out.flush();
out.close();
in.close();
socket.close();
mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 3000);
Log.d(TAG, "Socket Closed");
}
} catch (Exception e) {
Log.d(TAG, "Error", e);
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
}
}
The last thing in the client is the Callback interface. We have it in the TCPClient class in the end:
/**
* Callback Interface for sending received messages to 'onPublishProgress' method in AsyncTask.
*
*/
public interface MessageCallback {
/**
* Method overriden in AsyncTask 'doInBackground' method while creating the TCPClient object.
* #param message Received message from server app.
*/
public void callbackMessageReceiver(String message);
}

Related

smack message sent status

I am using smack and openfire for create chat app in android .
for message status I have no problem with delivered and displayed message in other
client (double check).
I will send a simple json message like bellow to sender:
{"delivery":timestapmp}
and parse it and double check messages with lower than timestamp that sent before.
the problem is about sent status (one check).
When i send message the server no response anything that message has sent .
is it possible in smack to send message with callback from server.
if possible and is it possible to send time server in callback response .
thanks .
private void acknowledgementFromServer(final Message message) throws StreamManagementException.StreamManagementNotEnabledException {
if (connection != null && connection.isSmEnabled()) {
connection.addStanzaIdAcknowledgedListener(message.getStanzaId(), new StanzaListener() {
#Override
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
MessageAsyncTask task = new MessageAsyncTask(packet.getStanzaId(), MSG_STATUS_SENT);
task.execute();
}
});
}
Hey you can do it like this.. call method every time you send message by passing that message as a parameter in above method
Note: Stream Management should be enabled for this to work, can be done like below:
DeliveryReceiptManager.setDefaultAutoReceiptMode(DeliveryReceiptManager.AutoReceiptMode.always);
ProviderManager.addExtensionProvider(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE, new DeliveryReceipt.Provider());
ProviderManager.addExtensionProvider(DeliveryReceiptRequest.ELEMENT, DeliveryReceipt.NAMESPACE, new DeliveryReceiptRequest.Provider());
According to my knowledge I have got up to this Inteface : ReceiptReceivedListener which is in smack 4.2
below is how I have implemented this :
private ReceiptReceivedListener receiptReceivedListener;
/**
* get DeliveryReceiptManager
*
* #return
*/
private DeliveryReceiptManager getDeliveryReceiptManager() {
if (deliveryReceiptManager == null && getConnection() != null) {
deliveryReceiptManager = DeliveryReceiptManager.getInstanceFor(getConnection());
}
return deliveryReceiptManager;
}
add Listener
getDeliveryReceiptManager().addReceiptReceivedListener(receiptReceivedListener);
Received the call back
receiptReceivedListener = new ReceiptReceivedListener() {
#Override
public void onReceiptReceived(Jid fromJid, Jid toJid, String receiptId, Stanza receipt) {
//TODO : on recieved status of message delivery
}
};
This will help you for sure
Below is the Interface for Smack 4.2 with full details :
/**
* Callback invoked when a new receipt got received.
* <p>
* {#code receiptId} correspondents to the message ID, which can be obtained with
* {#link org.jivesoftware.smack.packet.Stanza#getStanzaId()}.
* </p>
*
* #param fromJid the jid that send this receipt
* #param toJid the jid which received this receipt
* #param receiptId the message ID of the stanza(/packet) which has been received and this receipt is for
* #param receipt the receipt
*/
void onReceiptReceived(Jid fromJid, Jid toJid, String receiptId, Stanza receipt);

Socket Dies When Reestablishing Later

I have a client server model where the client runs on android. It establishes its tls sockets using the following code:.
(Everything the client does to login and relogin)
public class LoginAsync extends AsyncTask<Boolean, String, Boolean>
protected Boolean doInBackground(Boolean... params)
{
try
{
//only handle 1 login request at a time
synchronized(loginLock)
{
if(tryingLogin)
{
Utils.logcat(Const.LOGW, tag, "already trying a login. ignoring request");
onPostExecute(false);
return false;
}
tryingLogin = true;
}
//http://stackoverflow.com/a/34228756
//check if server is available first before committing to anything
// otherwise this process will stall. host not available trips timeout exception
Socket diag = new Socket();
diag.connect(new InetSocketAddress(Vars.serverAddress, Vars.commandPort), TIMEOUT);
diag.close();
//send login command
Vars.commandSocket = Utils.mkSocket(Vars.serverAddress, Vars.commandPort, Vars.expectedCertDump);
String login = Utils.currentTimeSeconds() + "|login|" + uname + "|" + passwd;
Vars.commandSocket.getOutputStream().write(login.getBytes());
//read response
byte[] responseRaw = new byte[Const.BUFFERSIZE];
int length = Vars.commandSocket.getInputStream().read(responseRaw);
//on the off chance the socket crapped out right from the get go, now you'll know
if(length < 0)
{
Utils.logcat(Const.LOGE, tag, "Socket closed before a response could be read");
onPostExecute(false);
return false;
}
//there's actual stuff to process, process it!
String loginresp = new String(responseRaw, 0, length);
Utils.logcat(Const.LOGD, tag, loginresp);
//process login response
String[] respContents = loginresp.split("\\|");
if(respContents.length != 4)
{
Utils.logcat(Const.LOGW, tag, "Server response imporoperly formatted");
onPostExecute(false); //not a legitimate server response
return false;
}
if(!(respContents[1].equals("resp") && respContents[2].equals("login")))
{
Utils.logcat(Const.LOGW, tag, "Server response CONTENTS imporperly formated");
onPostExecute(false); //server response doesn't make sense
return false;
}
long ts = Long.valueOf(respContents[0]);
if(!Utils.validTS(ts))
{
Utils.logcat(Const.LOGW, tag, "Server had an unacceptable timestamp");
onPostExecute(false);
return false;
}
Vars.sessionid = Long.valueOf(respContents[3]);
//establish media socket
Vars.mediaSocket = Utils.mkSocket(Vars.serverAddress, Vars.mediaPort, Vars.expectedCertDump);
String associateMedia = Utils.currentTimeSeconds() + "|" + Vars.sessionid;
Vars.mediaSocket.getOutputStream().write(associateMedia.getBytes());
Intent cmdListenerIntent = new Intent(Vars.applicationContext, CmdListener.class);
Vars.applicationContext.startService(cmdListenerIntent);
onPostExecute(true);
return true;
}
catch (CertificateException c)
{
Utils.logcat(Const.LOGE, tag, "server certificate didn't match the expected");
onPostExecute(false);
return false;
}
catch (Exception i)
{
Utils.dumpException(tag, i);
onPostExecute(false);
return false;
}
}
with the mksocket utility function being:
public static Socket mkSocket(String host, int port, final String expected64) throws CertificateException
{
TrustManager[] trustOnlyServerCert = new TrustManager[]
{new X509TrustManager()
{
#Override
public void checkClientTrusted(X509Certificate[] chain, String alg)
{
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String alg) throws CertificateException
{
//Get the certificate encoded as ascii text. Normally a certificate can be opened
// by a text editor anyways.
byte[] serverCertDump = chain[0].getEncoded();
String server64 = Base64.encodeToString(serverCertDump, Base64.NO_PADDING & Base64.NO_WRAP);
//Trim the expected and presented server ceritificate ascii representations to prevent false
// positive of not matching because of randomly appended new lines or tabs or both.
server64 = server64.trim();
String expected64Trimmed = expected64.trim();
if(!expected64Trimmed.equals(server64))
{
throw new CertificateException("Server certificate does not match expected one.");
}
}
#Override
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
}
};
try
{
SSLContext context;
context = SSLContext.getInstance("TLSv1.2");
context.init(new KeyManager[0], trustOnlyServerCert, new SecureRandom());
SSLSocketFactory mkssl = context.getSocketFactory();
Socket socket = mkssl.createSocket(host, port);
socket.setKeepAlive(true);
return socket;
}
catch (Exception e)
{
dumpException(tag, e);
return null;
}
}
Here is the command listener service that gets started on successful login:
public class CmdListener extends IntentService
protected void onHandleIntent(Intent workIntent)
{
// don't want this to catch the login resposne
Utils.logcat(Const.LOGD, tag, "command listener INTENT SERVICE started");
while(inputValid)
{
String logd = ""; //accumulate all the diagnostic message together to prevent multiple entries of diagnostics in log ui just for cmd listener
try
{//the async magic here... it will patiently wait until something comes in
byte[] rawString = new byte[Const.BUFFERSIZE];
int length = Vars.commandSocket.getInputStream().read(rawString);
if(length < 0)
{
throw new Exception("input stream read failed");
}
String fromServer = new String(rawString, 0, length);
String[] respContents = fromServer.split("\\|");
logd = logd + "Server response raw: " + fromServer + "\n";
//check for properly formatted command
if(respContents.length != 4)
{
Utils.logcat(Const.LOGW, tag, "invalid server response");
continue;
}
//verify timestamp
long ts = Long.valueOf(respContents[0]);
if(!Utils.validTS(ts))
{
Utils.logcat(Const.LOGW, tag, "Rejecting server response for bad timestamp");
continue;
}
//just parse and process commands here. not much to see
}
catch (IOException e)
{
Utils.logcat(Const.LOGE, tag, "Command socket closed...");
Utils.dumpException(tag, e);
inputValid = false;
}
catch(NumberFormatException n)
{
Utils.logcat(Const.LOGE, tag, "string --> # error: ");
}
catch(NullPointerException n)
{
Utils.logcat(Const.LOGE, tag, "Command socket null pointer exception");
inputValid = false;
}
catch(Exception e)
{
Utils.logcat(Const.LOGE, tag, "Other exception");
inputValid = false;
}
}
//only 1 case where you don't want to restart the command listener: quitting the app.
//the utils.quit function disables BackgroundManager first before killing the sockets
//that way when this dies, nobody will answer the command listener dead broadcast
Utils.logcat(Const.LOGE, tag, "broadcasting dead command listner");
try
{
Intent deadBroadcast = new Intent(Const.BROADCAST_BK_CMDDEAD);
sendBroadcast(deadBroadcast);
}
catch (Exception e)
{
Utils.logcat(Const.LOGE, tag, "couldn't broadcast dead command listener... leftover broadacast from java socket stupidities?");
Utils.dumpException(tag, e);
}
}
And here is the background manager that signs you in when you switch from wifi to lte, lte to wifi, or when you come out of the subway from nothing to lte:
public class BackgroundManager extends BroadcastReceiver
{
private static final String tag = "BackgroundManager";
#Override
public void onReceive(final Context context, Intent intent)
{
if(Vars.applicationContext == null)
{
//sometimes intents come in when the app is in the process of shutting down so all the contexts won't work.
//it's shutting down anyways. no point of starting something
return;
}
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if(Vars.uname == null || Vars.passwd == null)
{
//if the person hasn't logged in then there's no way to start the command listener
// since you won't have a command socket to listen on
Utils.logcat(Const.LOGW, tag, "user name and password aren't available?");
}
String action = intent.getAction();
if(action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
{
manager.cancel(Vars.pendingRetries);
new KillSocketsAsync().execute();
if(Utils.hasInternet())
{
//internet reconnected case
Utils.logcat(Const.LOGD, tag, "internet was reconnected");
new LoginAsync(Vars.uname, Vars.passwd).execute();
}
else
{
Utils.logcat(Const.LOGD, tag, "android detected internet loss");
}
//command listener does a better of job of figuring when the internet died than android's connectivity manager.
//android's connectivity manager doesn't always get subway internet loss
}
else if (action.equals(Const.BROADCAST_BK_CMDDEAD))
{
String loge = "command listener dead received\n";
//cleanup the pending intents and make sure the old sockets are gone before making new ones
manager.cancel(Vars.pendingRetries);
new KillSocketsAsync().execute(); //make sure everything is good and dead
//all of this just to address the stupid java socket issue where it might just endlessly die/reconnect
//initialize the quick dead count and timestamp if this is the first time
long now = System.currentTimeMillis();
long deadDiff = now - Vars.lastDead;
Vars.lastDead = now;
if(deadDiff < Const.QUICK_DEAD_THRESHOLD)
{
Vars.quickDeadCount++;
loge = loge + "Another quick death (java socket stupidity) occured. Current count: " + Vars.quickDeadCount + "\n";
}
//with the latest quick death, was it 1 too many? if so restart the app
//https://stackoverflow.com/questions/6609414/how-to-programatically-restart-android-app
if(Vars.quickDeadCount == Const.QUICK_DEAD_MAX)
{
loge = loge + "Too many quick deaths (java socket stupidities). Restarting the app\n";
Utils.logcat(Const.LOGE, tag, loge);
//self restart, give it a 5 seconds to quit
Intent selfStart = new Intent(Vars.applicationContext, InitialServer.class);
int pendingSelfId = 999;
PendingIntent selfStartPending = PendingIntent.getActivity(Vars.applicationContext, pendingSelfId, selfStart, PendingIntent.FLAG_CANCEL_CURRENT);
manager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+Const.RESTART_DELAY, selfStartPending);
//hopefully 5 seconds will be enough to get out
Utils.quit();
return;
}
else
{ //app does not need to restart. still record the accumulated error messages
Utils.logcat(Const.LOGE, tag, loge);
}
//if the network is dead then don't bother
if(!Utils.hasInternet())
{
Utils.logcat(Const.LOGD, tag, "No internet detected from commnad listener dead");
return;
}
new LoginAsync(Vars.uname, Vars.passwd).execute();
}
else if (action.equals(Const.ALARM_ACTION_RETRY))
{
Utils.logcat(Const.LOGD, tag, "login retry received");
//no point of a retry if there is no internet to try on
if(!Utils.hasInternet())
{
Utils.logcat(Const.LOGD, tag, "no internet for sign in retry");
manager.cancel(Vars.pendingRetries);
return;
}
new LoginAsync(Vars.uname, Vars.passwd).execute();
}
else if(action.equals(Const.BROADCAST_LOGIN_BG))
{
boolean ok = intent.getBooleanExtra(Const.BROADCAST_LOGIN_RESULT, false);
Utils.logcat(Const.LOGD, tag, "got login result of: " + ok);
Intent loginResult = new Intent(Const.BROADCAST_LOGIN_FG);
loginResult.putExtra(Const.BROADCAST_LOGIN_RESULT, ok);
context.sendBroadcast(loginResult);
if(!ok)
{
Utils.setExactWakeup(Const.RETRY_FREQ, Vars.pendingRetries);
}
}
}
}
The server is on a select system call to listen to its established sockets. It accepts new sockets using this code (C on Linux)
incomingCmd = accept(cmdFD, (struct sockaddr *) &cli_addr, &clilen);
if(incomingCmd < 0)
{
string error = "accept system call error";
postgres->insertLog(DBLog(Utils::millisNow(), TAG_INCOMINGCMD, error, SELF, ERRORLOG, DONTKNOW, relatedKey));
perror(error.c_str());
goto skipNewCmd;
}
string ip = inet_ntoa(cli_addr.sin_addr);
//setup ssl connection
SSL *connssl = SSL_new(sslcontext);
SSL_set_fd(connssl, incomingCmd);
returnValue = SSL_accept(connssl);
//in case something happened before the incoming connection can be made ssl.
if(returnValue <= 0)
{
string error = "Problem initializing new command tls connection from " + ip;
postgres->insertLog(DBLog(Utils::millisNow(), TAG_INCOMINGCMD, error, SELF, ERRORLOG, ip, relatedKey));
SSL_shutdown(connssl);
SSL_free(connssl);
shutdown(incomingCmd, 2);
close(incomingCmd);
}
else
{
//add the new socket descriptor to the client self balancing tree
string message = "new command socket from " + ip;
postgres->insertLog(DBLog(Utils::millisNow(), TAG_INCOMINGCMD, message, SELF, INBOUNDLOG, ip, relatedKey));
clientssl[incomingCmd] = connssl;
sdinfo[incomingCmd] = SOCKCMD;
failCount[incomingCmd] = 0;
}
The problem I'm having is when the client reconnects to the server from an ip address it has used recently, the socket on the client always seems to die after creation. If I retry again, it dies again. The only way to get it to connect is for the android app to kill and restart itself.
Example: on wifi at home with address 192.168.1.101. Connection ok. Switch to LTE on address 24.157.18.90. Reconnects me to the server ok. Come back home and get 192.168.1.101. The socket always dies until the app kills itself. Or if while I'm outside, I loose LTE because I take the subway, when I come out, I get the same problem. Note that each time, it will make a new socket. It will not somehow try to salvage the old one. The socket creation also seems to succeed. It's just as soon as the client wants to do a read on it, java says the socket is closed.
I put all the relevant code in its unobfuscated original form since it's my hobby project. I am out of ideas why this happens.
// http://stackoverflow.com/a/34228756
//check if server is available first before committing to anything
// otherwise this process will stall. host not available trips timeout exception
Socket diag = new Socket();
diag.connect(new InetSocketAddress(Vars.serverAddress, Vars.commandPort), TIMEOUT);
diag.close();
It is caused by these three pointless lines of code. The server gets a connection and an immediate read() result of zero.
There is no value in establishing a connection only to close it and then assume you can open another one. You should use the conection you just established. In general the correct way to establish whether any resource is available is to try to use it in the normal way. Techniques like the above are indistinguishable from attempts to predict the future.

LoganSquare and Salut libraries - Android Wifip2p

I try to using salut librari (wifip2p library) for Android.
All work fine, discover devices, connect host, connect clients...
But when I need send a message, I have a problem.
I follow Salut tutorial: Salut
I send a message, and in console say me tha tthe message is received.
09-13 13:21:58.633 21798-21906/app.ingenia.marse.ingeniamarse D/Salut: Listening for service data...
09-13 13:21:58.643 21798-21986/app.ingenia.marse.ingeniamarse V/Salut: A device is sending data...
09-13 13:21:58.675 21798-21986/app.ingenia.marse.ingeniamarse D/Salut: Successfully received data.
But I can't read the received message.
The sender function:
public void sendData(){
Message myMessage = new Message();
myMessage.description = "See you on the other side!";
network.sendToAllDevices(myMessage, new SalutCallback() {
#Override
public void call() {
Log.e(TAG, "Oh no! The data failed to send.");
}
});
}
Where I received data:
#Override
public void onDataReceived(Object data) {
Log.d(TAG, "Received network data.");
try
{
Message newMessage = LoganSquare.parse((Message)data, Message.class);
Log.d(TAG, newMessage.description); //See you on the other side!
//Do other stuff with data.
}
catch (IOException ex)
{
Log.e(TAG, "Failed to parse network data.");
}
}
The code don't excecute the log received network data. And I need to change one line:
Message newMessage = LoganSquare.parse((Message)data, Message.class);
//to
Message newMessage = LoganSquare.parse(String.valueOf((Message)data), Message.class);
And the Message.class:
#JsonObject
public class Message{
/*
* Annotate a field that you want sent with the #JsonField marker.
*/
#JsonField
public String description;
/*
* Note that since this field isn't annotated as a
* #JsonField, LoganSquare will ignore it when parsing
* and serializing this class.
*/
public int nonJsonField;
#OnJsonParseComplete
void onParseComplete() {
Log.d("PARSED", "onParseComplete: PARSED!!");
}
/*
* Optional callback method to do something before your
* object serializes.
*/
#OnPreJsonSerialize
void onPreSerialize() {
Log.d("SERIALIZED", "onParseComplete: SERIALIZED!!");
}
}
The two logs in message.class nor they have executed
It is because according to documentation, onDataRecieve() is not executed as data.isEmpty() returns true. Check what i mean from here.

Using mockito to test function that uses a context

Android Studio 2.1.2
I am trying to test getJsonFromResource which calls loadNewsFeed.
I want to be able to test 2 cases 1 where loadNewsFeed will return an empty string and the other where it will return some json string.
So I am trying to mock the loadNewsFeed function to return an empty string. However, when the concrete getJsonFromResource is called it will call the real loadNewsFeed and cause a null pointer exception.
This is what I have tried in my test comments explaining what I am doing:
#Test
public void shouldFailIfJSONStringIsEmpty() throws Exception {
/* Mock Context class */
Context context = mock(Context.class);
/* initialize the concrete parseNewsFeed passing in the fake context */
ParseNewsFeed parseNewsFeed = new ParseNewsFeed(context);
/* Create a mock of the parseNewsFeed so a fake call to loadNewsFeed will return an empty string */
ParseNewsFeed mockParseNewsFeed = mock(ParseNewsFeed.class);
/* Mock the events that will be verified */
ParseNewsFeedContract.Events<Status> mockEvents = mock(ParseNewsFeedContract.Events.class);
/* Return an empty string when loadNewsFeed is called */
when(mockParseNewsFeed.loadNewsFeed()).thenReturn("");
/* Called the concrete getJsonFromResource */
parseNewsFeed.getJsonFromResource(mockEvents);
/* verify that onNewsFailure was called once and onNewsSuccess was never called */
verify(mockEvents, times(1)).onNewsFailure(anyString());
verify(mockEvents, never()).onNewsSuccess(any(Status.class));
}
This is the class I am trying to test.
public class ParseNewsFeed implements ParseNewsFeedContract {
private Context mContext;
public ParseNewsFeed(Context context) {
if(context != null) {
Timber.d("mContext != null");
mContext = context;
}
}
/**
* Get the json from the local resource file and add to the cache to save loading each time
* #return the json in string representation
*/
#Override
public void getJsonFromResource(Events<Status> events) {
/* Get the json in string format */
final String jsonString = loadNewsFeed();
/* Check that is contains something */
if(!jsonString.isEmpty()) {
try {
final Status status = new Gson().fromJson(jsonString, Status.class);
if(status != null) {
Timber.d("url: %s", status.getResults().get(0).getMultimedia().get(0).getUrl());
events.onNewsSuccess(status);
}
else {
Timber.e("status == null");
events.onNewsFailure("Failed to get results from json");
}
}
catch (JsonSyntaxException e) {
Timber.e("Invalid JSON: %s", e.getMessage());
events.onNewsFailure(e.getMessage());
}
}
}
/**
* Opens and reads from the news_list and writes to a buffer
* #return return the json representation as a string or a empty string for failure
*/
public String loadNewsFeed() {
InputStream inputStream = mContext.getResources().openRawResource(R.raw.news_list);
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
InputStreamReader inputReader = new InputStreamReader(inputStream, "UTF-8");
BufferedReader bufferReader = new BufferedReader(inputReader);
int n;
while ((n = bufferReader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
inputStream.close();
}
catch (IOException ioException) {
return "";
}
return writer.toString();
}
}
First of all, the reason why your original code doesn't work is because there's no relationship between your two objects parseNewsFeed and mockParseNewsFeed, hence the stubbing that you do for the mockParseNewsFeed doesn't have any effect when you invoke parseNewsFeed.getJsonFromResource(mockEvents). Using spy as David Wallace suggested would work, but if I were you, I would rewrite the code a bit differently to make it even easier to test.
One observation is that the code in loadNewsFeed() method doesn't seem to have a strong relationship with the ParseNewsFeed class, so I'd extract this code into an object (e.g. NewsFeedLoader), and then have this object as a dependency of ParseNewsFeed class. Then you can mock this Loader easily (return "" or any string that you want when passing a Context and possibly the R.raw.news_list id as well). With this Loader class, you can even unit test it separately from the ParseNewsFeed, and being able to improve the Loader however you want to (e.g. a better way to read a raw resource) without affecting the ParseNewsFeed class.
Use when() and then() methods of your mocked context. It is actually described in example of official tutorial here.
#Mock
Context mMockContext;
#Test
public void readStringFromContext_LocalizedString() {
// Given a mocked Context injected into the object under test...
when(mMockContext.getString(R.string.hello_word))
.thenReturn(FAKE_STRING);
ClassUnderTest myObjectUnderTest = new ClassUnderTest(mMockContext);
// ...when the string is returned from the object under test...
String result = myObjectUnderTest.getHelloWorldString();
// ...then the result should be the expected one.
assertThat(result, is(FAKE_STRING));
It looks like you want to have a ParseNewsFeed object where the loadNewsFeed method has been stubbed, but other methods work correctly. The simplest way to get that would probably be to create a spy, something like
ParseNewsFeed spyParseNewsFeed = Mockito.spy(new ParseNewsFeed(context));
Mockito.doReturn("").when(spyParseNewsFeed).loadNewsFeed();

Android Connection is made but no Object is sent

I am trying to send an object to a server i wrote. I have only just learnt about AsyncTask and am trying to use it in order to keep networking tasks off the main thread. It is currently making the connection but is not receiving the object. I know the server is fine because I have tested it using code written to send the object from my laptop. Also I am not receiving any error messages.
Code:
private class sendToServer extends AsyncTask<UserObject, Integer, Double> {
#Override
protected Double doInBackground(UserObject...userObjects) {
ObjectOutputStream oos = null;
String Header = "GPSUpdate";
String Userid = "07000000001";
String Latitude = "6.00";
String Longitude = "6.00";
try{
Socket socket = new Socket("igor.gold.ac.uk", 3000);
oos = new ObjectOutputStream(socket.getOutputStream());
UserObject [] userObject = new UserObject[1];
userObject[0] = new UserObject();
userObject[0].setHeader(Header);
userObject[0].setUserid(Userid);
userObject[0].setLatitude(Latitude);
userObject[0].setLongitude(Longitude);
oos.writeObject(userObject[0]);
oos.flush();
oos.close();
}
catch(Exception e){
}
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
}
#Override
protected void onPostExecute(Double result) {
}
}
Also the code to send the object works when ran from my laptop. Do I have to set permissions or anything different if I'm using AsyncTask to handle network operations.
Thanks for any help given.
You have an empty block catching all exceptions! At least log what's going on in there...
Does your app have the INTERNET permission declared in its manifest?
Does UserObject implement Serializable?
I found the problem it was my own simple mistake. All the code was fine but the Object being sent was in a different package on the server than the one being sent. Updated them so they are both in the same package in their respective location.

Categories

Resources