I am trying to keep a tcp connection to a server alive even while the phone goes into sleep mode. I have searched everywhere and tried everything. This problem occurs on some phones and not others which is kind of random.
So basically one client sends a request to the server, then the server sends the request to another client. What happens is that the receiving client doesn't get the request at all. I have tested this with a debugger and the next line of code after the read never gets called. It is important for the device to receive the message right away. I am wondering how viber is achieving this. I thought about google cloud messaging but i would have to re-implement a lot, also according to the documentation, even with google cloud messaging the message doesn't necessarily reach the destination right away.
here is my code:
class BackgroundReadThread extends Thread {
#Override
public void run()
{
while(connectedToServer)
{
try
{
int bytesRead=0;
if(myWifiLock!=null && !myWifiLock.isHeld())
myWifiLock.acquire();
byte val=(byte)myInputStream.read();
myWakeLock.acquire();//this line never gets called when in sleep
if(val==-1)
{
unexpectedDisconnectionFromServer();
if(myWifiLock!=null && myWifiLock.isHeld())
myWifiLock.release();
myWakeLock.release();
return;
}
bytesRead=myInputStream.read(myBuffer, 0, bufferSize);
if(bytesRead<1)
{
unexpectedDisconnectionFromServer();
if(myWifiLock!=null && myWifiLock.isHeld())
myWifiLock.release();
myWakeLock.release();
return;
}
byte[] dataArray=Arrays.copyOfRange(myBuffer,0,bytesRead);
ByteBuffer data=ByteBuffer.allocate(bytesRead+1).put(val).put(dataArray);
myParent.invokeReceiveAction(data, bytesRead+1);
}
catch (IOException e)
{
myWakeLock.acquire();
unexpectedDisconnectionFromServer();
e.printStackTrace();
}
finally
{
if(myWifiLock!=null && myWifiLock.isHeld())
myWifiLock.release();
if(myWakeLock!=null && myWakeLock.isHeld())
myWakeLock.release();
}
}
}
}
EDIT: forgot to mention that this code is running in a service
I have no idea why but the problem only occurs sometimes and it only occurs on the debug version of the application. I have tested the release version of the application and it never failed once on any of the phones ive tested it on. So i guess the problem is with the debug version although i have no idea why. Hope this helps someone having similar problems.
Related
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.
I'm using Azure Mobile Services in my android application to add authentication to the app, via Facebook and Google. However, every single time I attempt to login from the app, I receive the following error:
"com.microsoft.windowsazure.mobileservices.MobileServiceException: Logging >in with the selected authentication provider is not enabled".
No other errors occur. This is my code:
private void authenticate(boolean bRefreshCache)
throws ClientProtocolException, IOException {
bAuthenticating = true;
if (bRefreshCache || !loadUserTokenCache(mClient)) {
mClient.login(MobileServiceAuthenticationProvider.Facebook,
new UserAuthenticationCallback() {
#Override
public void onCompleted(MobileServiceUser user,
Exception exception,
ServiceFilterResponse response) {
synchronized (mAuthenticationLock) {
if (exception == null) {
cacheUserToken(mClient.getCurrentUser());
Log.i("MappingRoadConditions",
"authenticating");
createAndShowDialog(String.format(
"You are now logged in - %1$2s",
user.getUserId()), "Success");
} else {
createAndShowDialog(exception.getMessage(),
"Login Error");
}
bAuthenticating = false;
mAuthenticationLock.notifyAll();
}
}
});
} else {
// Other threads may be blocked waiting to be notified when
// authentication is complete.
synchronized (mAuthenticationLock) {
bAuthenticating = false;
mAuthenticationLock.notifyAll();
}
}
}
The function for logging in by Google is exactly the same, other than the name of the provider of course.
1) I have tried troubleshooting by logging in through the browser and I can login perfectly well using both Facebook and Google.
2) I have added the internet permission in the manifest file.
3) I have also tried testing the app by changing the internet connections, in case it's a network connection problem but to no avail. I am able to login perfectly well through the browser on the same internet connection.
Any ideas on what could be happening?
I struggled with this for a while when moving my working code over into a fresh app
It seems that after I eliminated the provider app connection as your problem (I used the javascript html client in parallel ) I needed to go back to basics because I found this similar question
Check your Manifest
I also had this issue just happen on a successful build - the ADB bridge had failed and the emulator could not connect to the internet (I had switched networks)
This error code is not descriptive, but Azure seems to assume if it can't connect to a provider then you didn't set it up!
I developed an android turn based game and so far I can make the first move but when I want the user would wait for the second player to play the app crashes.
What I tried to do is when the player finishes his move I call a function like this:
public void TheThread()
{
boolean fy=false;
while(!fy)
{
if(CheckMove2())
fy=true;
}
}
The checkmove2 function connects the parse table and check if is there a turn and return boolean.
I beleive this is not the right way to do it,thanks for your help.
Edit:
ChecKmove2() function:
private boolean CheckMove2() {
fx=false;
ParseQuery query = new ParseQuery("serverturn");
query.whereEqualTo("Receiver", Sender);
query.getFirstInBackground(new GetCallback() {
public void done(ParseObject updatePO, ParseException ParseError) {
if(ParseError == null){
fx=true;
String objID;
x=updatePO.getInt("x");
y=updatePO.getInt("y");
try {
updatePO.delete();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
///here comes the game algorithm.
if(fx)
return true;
return false;
}
Edit: the app not crashes its just stop and alert: "The app is not responding" and then asks if I want to wait or close the app.The checkmove function works perfectly I checked it with other devices,the problem is to use this checkmove function in a while loop so the user would wait for the opponent turn.
Your application is crashing because in that scenario you've caused an endless loop (if there are no other moves waiting). So the OS will, most likely force close your application because it thinks its become unresponsive.
What you're better off doing is this:
Create a background service with an AyncTask to check for other players moves at specific intervals (10 - 15 mins maybe?)
Once a move is found. Alert the player to it by using a Notification which would link to your app via an Intent
As a side note, you should always use background threads/async tasks etc to talk to networks.
On your place i would do next:
In parse cloudCode afterSave or beforeSave of the object you are waiting for, send a socket message to your app when the needed object is updated. For example using pubnub.com
I think this is a decent solution, but running some loopy checking logic seemes so unprofessional.
In general it works like this:
two players subscribe to a channel
when one finishes move, from cloud code send a message to this
channel
catch it in your app.
The message can trigger update from parse or can contain the needed data itself.
My Question is: Can Android 4.3 (client) have active connections with multiple BLE devices (servers)? If so, how can I achieve it?
What I did so far
I try to evaluate what throughput you can achieve using BLE and Android 4.3 BLE API. In addition I also try to find out how many devices can be connected and active at the same time. I use a Nexus 7 (2013), Android 4.4 as master and TI CC2540 Keyfob as slaves.
I wrote a simple server software for the slaves, which transmits 10000 20Byte packets through BLE notifications. I based my Android App on the Application Accelerator from the Bluetooth SIG.
It works well for one device and I can achieve around 56 kBits payload throughput at a Connection Interval of 7.5 ms. To connect to multiple slaves I followed the advice of a Nordic Employee who wrote in the Nordic Developer Zone:
Yes it's possible to handle multiple slaves with a single app. You would need to handle each slave with one BluetoothGatt instance. You would also need specific BluetoothGattCallback for each slave you connect to.
So I tried that and it partly works. I can connect to multiple slaves. I can also register for notifications on multiple slaves. The problem begins when I start the test. I receive at first notifications from all slaves, but after a couple Connection Intervals just the notifications from one device come trough. After about 10 seconds the other slaves disconnect, because they seem to reach the connection time-out. Sometimes I receive right from the start of the test just notifications from one slave.
I also tried accessing the attribute over a read operation with the same result. After a couple of reads just the answers from one device came trough.
I am aware that there are a few similar questions on this forum: Does Android 4.3 support multiple BLE device connections?, Has native Android BLE GATT implementation synchronous nature? or Ble multiple connection. But none of this answers made it clear for me, if it is possible and how to do it.
I would be very grateful for advice.
I suspect everyone adding delays is just allowing the BLE system to complete the action you have asked before you submit another one. Android's BLE system has no form of queueing. If you do
BluetoothGatt g;
g.writeDescriptor(a);
g.writeDescriptor(b);
then the first write operation will immediately be overwritten with the second one. Yes it's really stupid and the documentation should probably actually mention this.
If you insert a wait, it allows the first operation to complete before doing the second. That is a huge ugly hack though. A better solution is to implement your own queue (like Google should have). Fortunately Nordic have released one for us.
https://github.com/NordicSemiconductor/puck-central-android/tree/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt
Edit: By the way this is the universal behaviour for BLE APIs. WebBluetooth behaves the same way (but Javascript does make it easier to use), and I believe iOS's BLE API also behaves the same.
Re visting the bluetooth-lowenergy problem on android: I am still using delays.
The concept: after every major action that provokes the BluetoothGattCallback (e.g. conenction, service discovery, write, read) a dealy is needed. P.S. have a look at Google example on BLE API level 19 sample for connectivity to understand how Broadcasts should be sent and get some general understanding etc...
Firstly, scan (or scan) for BluetoothDevices, populate the connectionQueue with desired devices and call initConnection().
Have a look on the following example.
private Queue<BluetoothDevice> connectionQueue = new LinkedList<BluetoothDevice>();
public void initConnection(){
if(connectionThread == null){
connectionThread = new Thread(new Runnable() {
#Override
public void run() {
connectionLoop();
connectionThread.interrupt();
connectionThread = null;
}
});
connectionThread.start();
}
}
private void connectionLoop(){
while(!connectionQueue.isEmpty()){
connectionQueue.poll().connectGatt(context, false, bleInterface.mGattCallback);
try {
Thread.sleep(250);
} catch (InterruptedException e) {}
}
}
Now if all is good, you have made connections and BluetoothGattCallback.onConnectionStateChange(BluetoothGatt gatt, int status, int newState) has been called.
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
switch(status){
case BluetoothGatt.GATT_SUCCESS:
if (newState == BluetoothProfile.STATE_CONNECTED) {
broadcastUpdate(BluetoothConstants.ACTION_GATT_CONNECTED, gatt);
}else if(newState == BluetoothProfile.STATE_DISCONNECTED){
broadcastUpdate(BluetoothConstants.ACTION_GATT_DISCONNECTED, gatt);
}
break;
}
}
protected void broadcastUpdate(String action, BluetoothGatt gatt) {
final Intent intent = new Intent(action);
intent.putExtra(BluetoothConstants.EXTRA_MAC, gatt.getDevice().getAddress());
sendBroadcast(intent);
}
P.S. sendBroadcast(intent) might need to be done like this:
Context context = activity.getBaseContext();
context.sendBroadcast(intent);
Then the broadcast is received by BroadcastReceiver.onReceive(...)
public BroadcastReceiver myUpdateReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(BluetoothConstants.ACTION_GATT_CONNECTED.equals(action)){
//Connection made, here you can make a decision: do you want to initiate service discovery.
// P.S. If you are working with multiple devices,
// make sure that you start the service discovery
// after all desired connections are made
}
....
}
}
After doing whatever you want in the broadcast receiver, here is how I continue:
private Queue<BluetoothGatt> serviceDiscoveryQueue = new LinkedList<BluetoothGatt>();
private void initServiceDiscovery(){
if(serviceDiscoveryThread == null){
serviceDiscoveryThread = new Thread(new Runnable() {
#Override
public void run() {
serviceDiscovery();
serviceDiscoveryThread.interrupt();
serviceDiscoveryThread = null;
}
});
serviceDiscoveryThread.start();
}
}
private void serviceDiscovery(){
while(!serviceDiscoveryQueue.isEmpty()){
serviceDiscoveryQueue.poll().discoverServices();
try {
Thread.sleep(250);
} catch (InterruptedException e){}
}
}
Again, after a successful service discovery, BluetoothGattCallback.onServicesDiscovered(...) is called. Again, I send an intent to the BroadcastReceiver (this time with different action String) and it is now that you can start reading, writing and enabling notifications/indications...
P.S. If you are working with multiple devices, make sure that you start the reading, writing etc... stuff after all devices have reported that their services have been discovered.
private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();
private void startThread(){
if(initialisationThread == null){
initialisationThread = new Thread(new Runnable() {
#Override
public void run() {
loopQueues();
initialisationThread.interrupt();
initialisationThread = null;
}
});
initialisationThread.start();
}
}
private void loopQueues() {
while(!characteristicReadQueue.isEmpty()){
readCharacteristic(characteristicReadQueue.poll());
try {
Thread.sleep(BluetoothConstants.DELAY);
} catch (InterruptedException e) {}
}
// A loop for starting indications and all other stuff goes here!
}
BluetoothGattCallback will have all your incoming data from the BLE sensor. A good practice is to send a broadcast with the data to your BroadcastReceiver and handle it over there.
I am developing an app with BLE features myself. The way I managed to connect to multiple devices and turn on notifications was to implement delays.
So I make a new thread (in order not to block UI thread) and in the new thread connect and turn on notifications.
For example, after BluetoothDevice.connectGatt(); call Thread.sleep();
And add the same delay for read/write and enable/dissable notifications.
EDIT
Use wait like this so that Android dindn't reaise ANR
public static boolean waitIdle() {
int i = 300;
i /= 10;
while (--i > 0) {
if (true)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return i > 0;
}
Unfortunately notifications in the current Android BLE stack are a bit buggy. There are some hardcoded limits and I've found some stability issues even with a single device. (I read at one point that you could only have 4 notifications... not sure if that's across all devices or per device. Trying to find the source for that info now.)
I would try switching to a polling loop (say, poll the items in question 1/sec) and seeing if you find your stability increases. I would also consider switching to a different slave device (say a HRM or the TI SensorTag) to see if there is perhaps an issue with the slave-side code (unless you can test that against iOS or another platform and confirm it isn't part of the issue).
Edit: Reference for notification limitation
Rain is right in his answer, you need delays for pretty much everything when you work with BLE in Android. I developed several apps with it and it is really necessary. By using them you avoid a lot of crashes.
In my case, I use delays after every read/write command. Doing so, you ensure you receive the response from the BLE device almost always. I do something like this: (of course everything is done in a separate thread to avoid to much work on the main thread)
readCharacteristic(myChar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
myChar.getValue();
or:
myChar.setValue(myByte);
writeCharacteristic(myChar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
This is really useful when you read/write several characteristics in a row... As Android is enough fast to execute the commands almost instantly, if you don't use a delay between them you may get errors or incoherent values...
Hope it helps even if it is not exactly the answer to your question.
I encountered a very strange problem while sending files, such as pictures, text and zip files to server via ftp. Most of the time, it works fine. But sometimes, the server only has part of the file.
On Android, I use com.enterprisedt.net.ftp.
Here is the code piece to send a file:
public void ftpUploadFiles(ArrayList<String> fileList, boolean bDeleteAfterUploaded)
{
if(fileList.size() <= 0)
return;
// set up to transfer the files
FileTransferClient ftp = null;
try
{
//Make sure there is only FTP in the whole system at any given time.
synchronized(this)
{
// create client
ftp = new FileTransferClient();
// set remote host
ftp.setRemoteHost("xxxxxxx");
ftp.setUserName("xxxxxx");
ftp.setPassword("xxxxx");
// connect to the server
ftp.connect();
ftp.getAdvancedFTPSettings().setConnectMode(FTPConnectMode.PASV);
//1. Upload each file
for(int i = 0 ; i < fileList.size(); i++)
{
if(!FileKit.fileExist(fileList.get(i)))
continue;
ftp.uploadFile(fileList.get(i), FileKit.getFileName(fileList.get(i)));
if(bDeleteAfterUploaded)
FileKit.fileDelete(fileList.get(i));
}
ftp.disconnect();
} //End of synchronized
} catch (Exception e)
{
FileKit.handleException(e);
}
}
FileKit is a static wrap-up class for regular file functions. ftpUploadFiles() is called in a separate thread by an Intent so it can run in background. What seems to happen is, the ftp stops before finish transferring the file completely, so the server only gets part of the file. Based on the code above, what could possibly cause the problem? Or is it possible that is a issue with com.enterprisedt.net.ftp?
Thanks.
Your code appears to handle complete files only. So it should either succeed or fail for complete files only. That leaves only the com.enterprisedt.net.ftp package, which may send files in parts, for example when network failures occur.
There can be a problem if the transfer is interrupted due to network congestion, etc. "If the network connection is interrupted, the server may still think you are connected (as quit() has not been called). Hence a new connection and attempt to resume may fail for the reason given below." (https://enterprisedt.com/forums/viewtopic.php?t=960)
So I think that either the ftp software has a bug on network failure, or your code isn't handling the ftp software's handling of network failures.