I want to send a MMS using the stock MMS source. Before more explanation, I want to say that it does work on some android versions but 4.0.3 and 4.0.4.
In my service, I ask the device to enable the MMS network feature using the following code:
createWakeLock();
int result = mConnMgr.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, PhoneEx.FEATURE_ENABLE_MMS);
Log.v(TAG, "beginMmsConnectivity: result=" + result);
switch (result) {
case PhoneEx.APN_ALREADY_ACTIVE:
case PhoneEx.APN_REQUEST_STARTED:
acquireWakeLock();
return result;
}
throw new IOException("Cannot establish MMS connectivity");
On some devices (Xperia T running 4.0.3), it throws an exception because result equals PhoneEx.APN_TYPE_NOT_AVAILABLE. The MMS is enabled in my phone settings and I can send one with the stock mms app.
On other devices (HTC Desire S running 4.0.4), the problem is located a bit further, in the ConnectivityBroadcastReceiver. Here is my code:
private class ConnectivityBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
String action = intent.getAction();
mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
mOtherNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
networkAttempt++;
if (networkAttempt < APN_RETRY_ATTEMPTS) {
// Check availability of the mobile network.
if ((mNetworkInfo == null) || (mNetworkInfo.getType() != ConnectivityManager.TYPE_MOBILE_MMS)) {
// ERROR is located here, it returns TYPE_MOBILE :s
Log.v(TAG, " type is not TYPE_MOBILE_MMS, bail");
return;
}
}
...
As you can see in the comment, mNetworkInfo.getType() returns TYPE_MOBILE but I expect TYPE_MOBILE_MMS.
So, my question is the following: Did I make something wrong ? Or, Is there another way to do that ?
Ps: It works on devices running Android 2.3 to 3.2 and 4.1 and above.
It appears on some devices, with some providers, the TYPE_MOBILE isn't the default MMS gateway, you have to use another one. Here is the solution I found that solved my problem.
I hope this could help someone else.
// Take a wake lock so we don't fall asleep before the message is downloaded.
createWakeLock();
// Let's try every type
int result = -1;
int[] apnTypes = new int[] {ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_MOBILE_MMS, ConnectivityManager.TYPE_MOBILE_DUN, ConnectivityManager.TYPE_MOBILE_HIPRI, ConnectivityManager.TYPE_MOBILE_SUPL};
for (int i=0; i<apnTypes.length; i++)
{
result = mConnMgr.startUsingNetworkFeature(apnTypes[i], PhoneEx.FEATURE_ENABLE_MMS);
Log.v(TAG, "beginMmsConnectivity: result=" + result);
switch (result)
{
case PhoneEx.APN_ALREADY_ACTIVE:
case PhoneEx.APN_REQUEST_STARTED:
acquireWakeLock();
return result;
}
}
// None found
throw new IOException("Cannot establish MMS connectivity");
Related
I am trying to detect 5G network. I use the telephony manager to get NETWORK_TYPE. Even if I am in 5G network coverage and my phone shows 5G, I do not get NETWORK_TYPE_NR. The NETWORK_TYPE is always 13 i.e. LTE.
The Phones Engineering service mode shows NR related data.
Is there any way to detect NR (Standalone or Non-Standalone) mode?
I also need to get the Cell Information for NR data. I use telephonyManager.getAllCellinfo(), but I never get an instance of cellinfonr.
Any help is appreciated. Thanks
I faced the same problem for a few weeks ago. In my case, I want to detect 5G network on Galaxy S20-5G but the getDataNetworkType() always return 13 NETWORK_TYPE_LTE.
Following by netmonster-core strategy, and here is the code that I extract from them to solve my problem.
public boolean isNRConnected(TelephonyManager telephonyManager) {
try {
Object obj = Class.forName(telephonyManager.getClass().getName())
.getDeclaredMethod("getServiceState", new Class[0]).invoke(telephonyManager, new Object[0]);
// try extracting from string
String serviceState = obj.toString();
boolean is5gActive = serviceState.contains("nrState=CONNECTED") ||
serviceState.contains("nsaState=5") ||
(serviceState.contains("EnDc=true") &&
serviceState.contains("5G Allocated=true"));
if (is5gActive) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Here is full detector class from netmonster-core:
(DetectorLteAdvancedNrServiceState.kt)
we are using symbol ls4278 bluetooth scanner to integrate in android application. It is connected as keyboard and types scanned barcode in any edittext field...
After scanner OnKeyUp event is called.
public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
{
..
}
I was searching documentation and android sdk, but I can't found such one. But for LI4278 they have android sdk here : https://www.zebra.com/us/en/support-downloads/scanners/general-purpose-scanners/li4278.html
here is also documentation for sdk but LS4278 is not in supported device list.
Does anyone implemented LS4278 scanner in android devices?
The LS4278 product page is here: https://www.zebra.com/us/en/support-downloads/scanners/general-purpose-scanners/ls4278.html and lists support for the "Windows Scanner SDK" ONLY. The LS4278 was discontinued on September 24th 2012 so I am not surprised it does not have Android support. As you say, its successor, the LI4278 does have Android support. As the other answer states, if you want more control over how you receive data then I suggest trying SPP if the scanner supports it.
If it works as a bluetooth keyboard, then no support is needed. Just capture the key events, and react to the data when enter is pressed. Its just a mediocre experience and can mess with on screen keyboards and stop them from using an actual bluetooth keyboard. If the scanner supports SPP, you can pretty trivially parse the scan data out of it via bluetooth serial (I did this about 2 weeks ago).
BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
if(bta != null) {
Set<BluetoothDevice> devices = bta.getBondedDevices();
for (final BluetoothDevice device : devices) {
BluetoothClass btClass = device.getBluetoothClass();
if (btClass.getMajorDeviceClass() == 0x1f00) {
//Only look at devices which are considered uncategorized, so we don't screw up any bt headset, leyboard, mouse, etc
new DeviceThread(device).start();
}
}
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
if (state == BluetoothDevice.BOND_BONDED) {
new DeviceThread(device).start();
} else if (state == BluetoothDevice.BOND_NONE) {
DeviceThread thread = threadMap.get(device.getAddress());
if (thread != null) {
thread.interrupt();
}
}
}
}, filter);
}
private class DeviceThread extends Thread {
private BluetoothDevice device;
public DeviceThread(BluetoothDevice device) {
this.device = device;
threadMap.put(device.getAddress(), this);
}
#Override
public void run() {
try {
BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
socket.connect();
InputStream inputStream = socket.getInputStream();
while (!Thread.interrupted() && socket.isConnected()) {
inputStream.skip(5);
String data = "";
do {
int code = inputStream.read();
char character = (char) code;
data = data + character;
} while (inputStream.available() > 0);
data = data.substring(0, data.length() - 2);
if (scannerEventListener != null) {
scannerEventListener.onScan(data);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
Log.d("GABE", "Exiting thread");
}
}
This code will register for bluetooth devices being paired, then check and see if they're unknown device types (scanners don't have a device class). If so, it will start a thread to listen for that device. When its unbonded, it will interrupt that thread. On the thread it opens up a SPP connection to the device and waits for input. When it gets it, it parses the input and sends the result to a listener.
For this to work, the scanner needs to be in SPP mode. Some scanners support it, some don't, and how to set it into that mode varies (the one on my desk has a control barcode I need to scan to set the mode). Generally I would code for it to accept either type of input- hardware keyboard mode or SPP.
I'm trying to connect programmatically my device to for example on my Headsets... I had KitKat version and all worked perfect (Bluetooth always was connecting without problems autommatically) but since I've updated to Lolipop it doesn't. I'd like to know if there is any way to connect any paired device of my Android phone to Bluetooth when it turns on.
Since now I've this code (gets the Device name and Device Address) because I thought with it I could connect doing something like device.connect(MAC-Address); but it didn't work...
BluetoothAdapter bluetoothAdapter
= BluetoothAdapter.getDefaultAdapter();
Set < BluetoothDevice > pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device: pairedDevices) {
mDeviceName.add(device.getName());
mDeviceMAC.add(device.getAddress());
}
}
bluetoothClass.setDeviceName(mDeviceName);
bluetoothClass.setDeviceMac(mDeviceMAC);
Question
On my MotoG (KitKat) if I turn my Bluetooth it connects autommatically to device (if it's near and paired ofc...) but on my LG G3 I must go to Configuration/Bluetooth/Paired devices/ and there tap the device to connect... and I want to avoid this... should be possible?
I would like to know if there is any possibility to connect to specific Bluetooth just adding the Device name or Device MAC... More or less like Android does when I click on my device to connect it connects autommatically... I just want to get that CLICK event.
I know that Android should connect autommatically to a paired device but there's any exceptions that doesn not ... the only way to pair it it's doing the click... that's why I'm wondering if it's there a way to do it...
I've read and tested kcoppock answer but it still don't work ..
Any suggestion?
EDIT
The main thing that I wanted to do is to connect my Bluetooth autommatically but since I've read on Hey you answer... I figured it out and I know it's an Android bug, so the thing that I would like to do is select the paired devices and then click on the device that I want to connect (Without doing any Intent) and connect it, instead to go Configuration/Bluetooth/....
Btw I've read any answers on StackOverflow and I found something with Sockets are they used to connect Bluetooth?Could be it a solution?
Edit to answer latest question
You can avoid using an intent to search for paired devices. When connecting to a device that is not paired, a notification will pop up asking to pair the devices. Once paired this message should not show again for these devices, the connection should be automatic (according to how you have written your program).
I use an intent to enable bluetooth, and to make my device discoverable, I then set up my code to connect, and press a button to connect. In your case, you will need to ensure your accessories are discoverable also. In my case I use a unique UUID, and both devices must recognise this to connect. This can only be used if you are programming both devices, whether both are android or one android and one other device type.
Try this, and see if it solves your problem.
This answer is to the original question before it was edited to be another question.
I've edited my answer for clarity as I can see from the comments it is misleading. Your question has two parts.
On my MotoG (KitKat) if I turn my Bluetooth it connects autommatically
to device (if it's near and paired ofc...) but on my LG G3 I must go
to Configuration/Bluetooth/Paired devices/ and there tap the device to
connect... and I want to avoid this... should be possible?
This is less of a programming issue and more of a platform issue.
There is a well documented bug in Android 5.0 with Bluetooth not automatically connecting and many other BT issues. These issues continue with all the updates on 5.0. versions and is not fixed until the 5.1. upgrade.
http://www.digitaltrends.com/mobile/android-lollipop-problems/11/
http://forums.androidcentral.com/lg-g3/473064-bluetooth-streaming-choppy-lg-3-lollipop.html
First port of call is to update to 5.1
These issues have been addressed in the Lollipop update 5.1
http://www.reddit.com/r/Android/comments/306m3y/lollipop_51_bluetooth/
Edit:
I don't believe this is going to fix your problem of the automatic pairing, you wanted to know how to use BTGatt.
I've seen if I type device. to check what can I do it let me
connectGatt() means /.../
But I can't figure it out how to do this...
To use BluetoothGatt
https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html
This class provides Bluetooth GATT functionality to enable
communication with Bluetooth Smart or Smart Ready devices.
/.../
GATT capable devices can be discovered using the Bluetooth device
discovery or BLE scan process.
https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html
Here is a great example of how to use BluetoothGatt (it uses hear rate):
https://github.com/googlesamples/android-BluetoothLeGatt/blob/master/Application/src/main/java/com/example/android/bluetoothlegatt/BluetoothLeService.java
I have reproduced some of the code here, in case the link dies.
It basically follows similar lines to a regular bluetooth connection. You need to discover and find supported devices.
Monitor state, etc.
These are the two most pertinent features to gatt.
The callback:
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
The broadcast:
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the Heart Rate Measurement profile. Data parsing is
// carried out as per profile specifications:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
}
}
sendBroadcast(intent);
}
This question also has some relevant code that may help cut it down when learning:
BLuetooth Gatt Callback not working with new API for Lollipop
Now here's the rub. Are your devices bluetooth smart or smart ready?
This link gives a great list of smart devices. You will also find out when you implement your program.
http://www.bluetooth.com/Pages/Bluetooth-Smart-Devices-List.aspx
This is how i made this work using Java Reflection and BluetoothProfile:
Attributes:
private boolean mIsConnect = true;
private BluetoothDevice mDevice;
private BluetoothA2dp mBluetoothA2DP;
private BluetoothHeadset mBluetoothHeadset;
private BluetoothHealth mBluetoothHealth;
Call:
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.A2DP);
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.HEADSET);
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.HEALTH);
Listener:
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.A2DP) {
mBluetoothA2DP = (BluetoothA2dp) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothA2dp.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothA2DP, mDevice);
} else {
Method disconnect = BluetoothA2dp.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothA2DP, mDevice);
}
}catch (Exception e){
} finally {
}
} else if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothHeadset.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothHeadset, mDevice);
} else {
Method disconnect = BluetoothHeadset.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothHeadset, mDevice);
}
}catch (Exception e){
} finally {
}
} else if (profile == BluetoothProfile.HEALTH) {
mBluetoothHealth = (BluetoothHealth) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothHealth.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothHealth, mDevice);
} else {
Method disconnect = BluetoothHealth.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothHealth, mDevice);
}
}catch (Exception e){
} finally {
}
}
}
public void onServiceDisconnected(int profile) {
}
};
I hope this helps anyone trying to connect to Bluetooth Audio devices and headsets.
I have two different bluetooth apps. The first provides a service and listens to commands from the other commander app. I have a GT-I9100T phone and a GT-P5210 tablet. The tablet when acting at the listener works fine and the phone can see the UUID for my app. But when I run the phone as the listener, the UUID of the listener is not listed.
I filter the devices by my application UUID so that I know I am talking only to those devices with my application running.
My listening app looks like this (I get the same result if I use an insecure connection as well):
private final UUID GUID = UUID.fromString("3DEF793A-FA94-4478-AE56-108854B1EF4B");
// other stuff....
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(APP_NAME, GUID);
My commander app MainActivity looks likes this:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Log.i(TAG,"Action received: "+action);
if(action.equals(BluetoothDevice.ACTION_UUID)) {
BluetoothDevice btd = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i(TAG,"Received uuids for "+btd.getName());
Parcelable[] uuidExtra = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
StringBuilder sb = new StringBuilder();
List<String> uuids = new ArrayList<String>(uuidExtra.length);
if(uuidExtra != null) {
for (int i = 0; i < uuidExtra.length; i++) {
sb.append(uuidExtra[i].toString()).append(',');
uuids.add(uuidExtra[i].toString());
}
}
Log.i(TAG,"ACTION_UUID received for "+btd.getName()+" uuids: "+sb.toString());
ListContent.addItemWithUUIDs(btd, uuids);
}
}
}
My list content (I am using the master/detail template):
public static synchronized void addItem(BluetoothItem item) {
BluetoothDevice btd = item.mBluetoothDevice;
Log.i(TAG,"Attempting to add "+item.mBluetoothDevice.getName());
if(ITEMS.contains(item)) {
Log.i(TAG,item.mBluetoothDevice.getName()+" already in list");
return;
}
// Do we know this device?
Parcelable[] uuids = btd.getUuids();
Set<String> setUUIDs = new HashSet<String>();
StringBuilder sb = new StringBuilder();
if(uuids != null) {
for (Parcelable parcelable : uuids) {
sb.append(parcelable.toString()).append(',');
setUUIDs.add(parcelable.toString());
}
}
Log.v(TAG,"Device has uuids: "+sb.toString());
if ((btd.getUuids() != null && setUUIDs.contains(BluetoothItem.GUID.toLowerCase()))){
Log.i(TAG, "Found app device: " + btd.getName());
addItem(btd);
} else {
// If we don't know this device, perform sdp fetch of uuids
Log.i(TAG,"Requesting fresh UUIDs for: "+btd.getName());
// this is flushed when discovering finishes
CANDIDATES.add(btd);
}
}
public static synchronized void addItemWithUUIDs(BluetoothDevice btd, List<String> uuids) {
Log.i(TAG,"Attempting to add with uuids"+btd.getName()+" uuids: "+btd.getUuids());
if (uuids.contains(BluetoothItem.GUID)) {
Log.i(TAG, "Found app device: " + btd.getName());
addItem(btd);
} else {
Log.i(TAG, "Ignoring device " + btd.getName() + " without app guid");
}
}
When discovery is finished, this happens:
for (BluetoothDevice i : ListContent.CANDIDATES) {
Log.i(TAG,"Fetching UUIDs for "+i.getName());
i.fetchUuidsWithSdp();
}
ListContent.CANDIDATES.clear();
The logcat output when using the tablet as the commander and phone as listener:
DeviceListActivity(29524): Received uuids for GT-I9100T
DeviceListActivity(29524): ACTION_UUID received for GT-I9100T uuids:
0000110a-0000-1000-8000-00805f9b34fb,
00001105-0000-1000-8000-00805f9b34fb,
00001116-0000-1000-8000-00805f9b34fb,
0000112d-0000-1000-8000-00805f9b34fb,
0000112f-0000-1000-8000-00805f9b34fb,
00001112-0000-1000-8000-00805f9b34fb,
0000111f-0000-1000-8000-00805f9b34fb,
00001132-0000-1000-8000-00805f9b34fb,
I get the correct output with phone as commander and tablet as listener:
DeviceListActivity(23121): Received uuids for GT-P5210
DeviceListActivity(23121): ACTION_UUID received for GT-P5210 uuids:
00001105-0000-1000-8000-00805f9b34fb,
0000110a-0000-1000-8000-00805f9b34fb,
0000110c-0000-1000-8000-00805f9b34fb,
00001112-0000-1000-8000-00805f9b34fb,
00001115-0000-1000-8000-00805f9b34fb,
0000111f-0000-1000-8000-00805f9b34fb,
00001200-0000-1000-8000-00805f9b34fb,
3def793a-fa94-4478-ae56-108854b1ef4b,
As you can see, the GUID for my app is listed as the last item. I've tried making the devices discoverable and bonding and unbonding, but the GUID for my app is never returned for the phone. I have other non-Android devices that also use this GUID and they are discovered normally as well.
The phone is running 4.1.2 and the tablet is 4.2.2
I have an android app which connects to a server using a socket connection which is kept open while the app is active. If the phone gets inactive (lock screen) or the user presses the home button, the application closes the socket connection and reopens it if the app becomes visible again.
This Pattern works fine on most of the android phones we have (about 15 devices), but the Motorola Milestone, Defy, SE Xperia Arc and the LG Optimus One need very long (>10 secs) to detect if a Wifi is available after and connect to it. So to wait for the best network connection I use the following code (before opening the socket to the server):
public static boolean waitNetworkConnection(Context context, int retries) {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = getNetworkToTest(cm);
if (ni == null || !ni.isConnected()) {
// sleep a short while to allow system to switch connecting networks.
Tools.sleep(1000);
int counter = 0;
while (counter < retries && (ni == null || (ni.isAvailable() &&
!ni.isConnected()))) {
Tools.sleep(500);
ni = getNetworkToTest(cm);
counter++;
}
}
return (cm.getActiveNetworkInfo() != null &&
cm.getActiveNetworkInfo().isConnected());
}
and this method (use by the one above) to get the connection to test, which prefers a Wifi-Connection if one (not necessary connected) is available:
private static NetworkInfo getNetworkToTest(ConnectivityManager cm) {
NetworkInfo[] nis = cm.getAllNetworkInfo();
NetworkInfo ni = cm.getActiveNetworkInfo();
for (int i = 0; i < nis.length; i++) {
if (nis[i].getType() == 1 /* Wifi */ && nis[i].isAvailable()) {
ni = nis[i];
return(ni);
}
}
return(ni);
}
This works fine for most of the devices, but for the mentioned ones this very often fails and this method tells me to use a mobile network connection and the device switches the connection type while I open a socket connection which leads to a SocketException with a very generic error message so I'm unable to determine if the socket connection is caused by this issue or because of some other network error.
Simply doing a retry doesn't fix this either, as this breaks the handling for the other network errors because it then takes very long to detect a socket timeout (because it is checked twice).
Has anyone else ran into this problem (very slowing connect to Wifi) and has a solution for this?
Yes, this is a tricky problem. One option would be to wait for the right network state broadcast using a BroadcastReceiver.
As described here: How to detect when WIFI Connection has been established in Android?
And here: How can I monitor the network connection status in Android?
There is a project called droidfu that has a HTTP wrapper, that gets round the wi-fi / 3g issue.
Here is a snippet from the code for the BetterHttpRequestBase class:
public BetterHttpResponse send() throws ConnectException {
BetterHttpRequestRetryHandler retryHandler = new BetterHttpRequestRetryHandler(maxRetries);
// tell HttpClient to user our own retry handler
httpClient.setHttpRequestRetryHandler(retryHandler);
HttpContext context = new BasicHttpContext();
// Grab a coffee now and lean back, I'm not good at explaining stuff. This code realizes
// a second retry layer on top of HttpClient. Rationale: HttpClient.execute sometimes craps
// out even *before* the HttpRequestRetryHandler set above is called, e.g. on a
// "Network unreachable" SocketException, which can happen when failing over from Wi-Fi to
// 3G or vice versa. Hence, we catch these exceptions, feed it through the same retry
// decision method *again*, and align the execution count along the way.
boolean retry = true;
IOException cause = null;
while (retry) {
try {
return httpClient.execute(request, this, context);
} catch (IOException e) {
cause = e;
retry = retryRequest(retryHandler, cause, context);
} catch (NullPointerException e) {
// there's a bug in HttpClient 4.0.x that on some occasions causes
// DefaultRequestExecutor to throw an NPE, see
// http://code.google.com/p/android/issues/detail?id=5255
cause = new IOException("NPE in HttpClient" + e.getMessage());
retry = retryRequest(retryHandler, cause, context);
} finally {
// if timeout was changed with this request using withTimeout(), reset it
if (oldTimeout != BetterHttp.getSocketTimeout()) {
BetterHttp.setSocketTimeout(oldTimeout);
}
}
}
// no retries left, crap out with exception
ConnectException ex = new ConnectException();
ex.initCause(cause);
throw ex;
}