I'm making an app where the user needs to be connected to a local wifi network. When the user opens the app, I check to see if wifi is connected and then if they are connected to the right router.
I written my app such that CheckWifiConnection() is called after BroadcastReceivers on WIFI_STATE_CHANGED_ACTION, SCAN_RESULTS_AVAILABLE_ACTION, and SUPPLICANT_CONNECTION_CHANGED_ACTION.
My app crashes when the user starts up with wifi disabled. An alert comes up asking to turn on wifi. Once wifi is enabled, the BroadcastReceiver calls CheckWifiConnection() and crashes on info.getSSID() saying:
error receiving broadcast intent act=android.net.wifi.wifi_state_changed
It crashes because I'm in the process of connecting to a wifi network after the radio has turned on. If I simply put a Handler.postDelayed call in, then it works.
Here is the pertinent code.
A chunk of my manifest file:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
/********************* Wifi Related Functions **********************/
private boolean CheckWifiConnection() {
// Connect to Wifi if not enabled
if (wifiManager.isWifiEnabled() == false) {
Log.d(TAG,"Wifi not on");
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs Wifi On");
builder.setMessage("Is that OK?");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
wifiManager.setWifiEnabled(true);
Log.d(TAG,"Turning WIFI on");
}
});
builder.setNeutralButton("NO", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
// For the time being connect to WIFI anyway
// Probably change to a toast and exit program
wifiManager.setWifiEnabled(true);
}
});
checkWifiStatus = true;
builder.show();
return false;
}
Log.d(TAG,"Wifi is On");
// See if they are connected to configured router
WifiInfo info = wifiManager.getConnectionInfo();
Log.d(TAG,"Have Wifi Info");
if ( info.getSSID().matches(router) == false) {
// Do a scan to see if they can see us
wifiManager.startScan();
Log.d(TAG,"Starting Network Scan");
return false;
}
return true;
}
private BroadcastReceiver wifiConnectionChanged = new BroadcastReceiver() {
public void onReceive(Context c, Intent i){
if (checkWifiStatus && wifiManager.isWifiEnabled()) {
Log.d(TAG,"WIFI is now on");
checkWifiStatus = false;
CheckWifiConnection();
}
}
};
Is there a way to check if I am in the process of connecting to a router and/or a BroadcastReceiver for when I connect to a Wifi network? I feel like putting a pause in my code is admitting defeat, and likely to cause problems down the line.
Also, If there is a better way of doing all of these checks, I am open to suggestions.
NagarjunaReddy, your link led me on the right path to find the solution. If the wifi antenna is in the process of connecting, then info is not null, but info.getSSID() is. Thus it appears to be better to write:
WifiInfo info = WifiManager.getConnectionInfo();
if (info.getSSID() != null) {
String ssid = info.getSSID();
...
}
I don't know if it would be overkill to write:
if (info != null && info.getSSID() != null)
Also, the BroadcastReceiver that I need to monitor when a connection is made is
WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION. It turns out that I had a copy and paste error in my onResume(), and I wasn't really registering WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.
Related
My android application is based on network connection i.e WIFI/Mobile Network. It works fine when my mobile is connected to internet but when internet connection disconnected it stops working (obesely) and it still stop working after my mobile again connected to internet.
I wish to (re)start my application automatically whenever internet connection is (re)established.
You can check the network state using broadcast receiver. Whenever the network is available, you can start your application.
First, create a background service and start your service when the device boots up. Now, in this service, register a broadcast receiver and monitor the network state. If the network is available, you can start your application; and if unavailable, you can close it.
Please refer to the code below for broadcast receiver.
public class BroadCastSampleActivity extends Activity
{
/** Called when the activity is first created. */
#Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.registerReceiver(this.mConnReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
private BroadcastReceiver mConnReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, Intent intent)
{
boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
String reason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
boolean isFailover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
NetworkInfo currentNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
NetworkInfo otherNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
if(currentNetworkInfo.isConnected())
{
Toast.makeText(getApplicationContext(), "Connected", Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(getApplicationContext(), "Not Connected", Toast.LENGTH_LONG).show();
}
}
};
}
I think you must need to checking continue for network connections, that means you need to check for internet connection in background tasks. Android Services is better option for that, create one Service and start it when your app starts, inside that just do one code and that is for checking Internet Connectivity, when it lost, do some task and when it found you can do whatever you want. So I suggest you to use services and get your task done.
Here are some links to refer.
http://www.tutorialspoint.com/android/android_services.htm
http://www.vogella.com/tutorials/AndroidServices/article.html
I think you should create a spread thread or service in background for checking network connection after some interval . use following code in thread or service whatever you want to create .
NetworkInfo i = conMgr.getActiveNetworkInfo();
if (i == null)
return false;
if (!i.isConnected())
return false;
if (!i.isAvailable())
return false;
return true;
I have written the following code for detecting the network status from within the BroadcastReceiver. I start a service when the network is available and stop the service when the network is not available.
I have the following class level variable.
private boolean IsNetworkAlreadyConnected = false;
Within onCreate method of the main class I start the service if the internet is available.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (InternetConnectivity.isConnected(MainActivity.this)) {
IsNetworkAlreadyConnected = true;
Intent timerIntent = new Intent(getBaseContext(), InActivityTimer.class);
startService(timerIntent);
}
}
and below is the code for my BroadcastReceiver in the same class,
public class mConnectivityCheckReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals("android.net.conn.CONNECTIVITY_CHANGE")) {
try {
boolean networkAvailable = InternetConnectivity.isConnected(context);
if (networkAvailable) {
if (!IsNetworkAlreadyConnected) {
Intent timerIntent = new Intent(getBaseContext(), InActivityTimer.class);
startService(timerIntent);
IsNetworkAlreadyConnected = true;
}
else {
Log.d("KC_HomeActivity", "Network was already connected. No need to start service again.");
}
}
else {
Log.d("KC_HomeActivity", "Network Disconnected. Service Stopped.");
IsNetworkAlreadyConnected = false;
Intent timerIntent = new Intent(getBaseContext(), InActivityTimer.class);
stopService(timerIntent);
}
}
catch (Exception e) {
}
}
}
};
When both Mobile data and Wifi are turned on then the service is started from onCreate method and it is not started again in the BroadcastReceiver but when I turn off the Wifi the Android changes the network mode to Mobile Data but for few seconds there is no internet connectivity and the service is stopped and then started again. I don't want to do this. If there is no connectivity only then the service should be stopped. If the network is shifting from Wifi to Mobile Data then the service should not be stopped.
Note: To check the internet connectivity I am using,
NetworkInfo info = InternetConnectivity.getNetworkInfo(context);
return (info != null && info.isConnectedOrConnecting());
Network connections aren't that precise. You should make it relax a bit, or you'll pull your hair out.
I would implement a smoothing function from the broadcasts. When you get a connectivity change notification, set a timeout for like 15 seconds. At that time, check your status and either start, stop, or do nothing. If another broadcast comes in, clear the first and reset for another 15 seconds. That will give the device time to reconnect.
When I press a button on my app to open an image it shows a progress bar of the image being downloaded and then it opens. If there is no wifi connection it displays an error message saying "No wifi". I have used the code below to check for a wifi connection:
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork.isConnectedOrConnecting();
boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
I want to implement an error message that is displayed when the connection is lost. So if you click on the image and while its downloading you turn off the wifi then it displays a message saying "wifi disconnected".
Is this possible? Whether a connection is lost or there is no wifi all you are doing is checking if there is a connection available which is the same thing
You need to use a BroadcastReceiver that will be triggered when the connectivity status for Wi-Fi has changed.
Set following things before registering BroadcastReceiver:
private class ConnectionChangeReceiver extends BroadcastReceiver {
public void onReceive( Context context, Intent intent ) {
Log.d(tag, "Inside Broadcast Reciever");
CheckWifiStatus();
}
}
private void RegisterWifiWatcher()
{
if(wifiWatcher == null)
wifiWatcher = new ConnectionChangeReceiver();
final IntentFilter intentFilter= new IntentFilter();
intentFilter.addAction("android.net.wifi.WIFI_STATE_CHANGED");
intentFilter.addAction("android.net.wifi.STATE_CHANGE");
registerReceiver(wifiWatcher, intentFilter);
}
WIFI_STATE_CHANGED :
Broadcast intent action indicating that Wi-Fi has been enabled, disabled, enabling, disabling, or unknown.
Permissions in Manifest :
<user-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<user-permission android:name="android.permission.ACCESS_WIFI_STATE" />
NOTE:
The broadcast intents that we receive for the different WiFi states have extras along with them that you can access to determine the different states of the WiFi connection.
Sure its possible. Adding to the above answer another option:
You can check with your code if wifi is available first.
Then start downloading the image and if you catch an exception during that time, toast the message wi-fi disconnected.
I've seen a couple of BroadcastReciever examples to detect wifi disconnects but none of them seem to work correctly (triggering twice for each disconnect for example) and none mention checking against an ssid, is this even possible?
So just to clarify, I want to detect disconnection from a particular ssid. An actual disconnect and not wifi being disabled on the device.
Thanks
EDIT: Re-opening as nothing works on both the devices we have to test.
NETWORK_STATE_CHANGED_ACTION was the answer in the end. The device having the problem registering this event started working when another app (which would also be listening for similar events) was uninstalled! No idea how or why an app could block events registering with another app. The final solution ended up being;
String action = intent.getAction();
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION))
{
WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
NetworkInfo.State state = networkInfo.getState();
if(state == NetworkInfo.State.CONNECTED)
{
String connectingToSsid = manager.getConnectionInfo().getSSID().replace("\"", "");
WifiStateHistory.recordConnectedSsid(connectingToSsid);
//connected
}
if(state == NetworkInfo.State.DISCONNECTED)
{
if(manager.isWifiEnabled())
{
String disconnectedFromSsid = WifiStateHistory.getLastConnectedSsid();
//disconnected
}
}
}
Are you sure there are twice notification for same state? There are always two phase of disconnection:
WifiManager.WIFI_STATE_DISABLING
WifiManager.WIFI_STATE_DISABLED
My code to detect (and rebroadcast) connections and disconnects (not by disabling wifi) and including the SSID as an extra is as follows. Most of what I've read suggested using SUPPLICANT_CONNECTION_CHANGE_ACTION but this just did not work correctly, it would seemingly only fire when disabling/enabling wifi on my device (Nexus 4) and not during connections. The only problem is on first run of the app as it won't record the current ssid so doesn't know what the ssid of the network that has just been connected. Any ideas around this?
public class EventMapper extends BroadcastReceiver
{
private static String lastConnectedSsid = "";
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION))
{
SupplicantState state = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
if(state == SupplicantState.COMPLETED)
{
WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
lastConnectedSsid = manager.getConnectionInfo().getSSID().replace("\"", "");
Intent newIntent = new Intent();
newIntent.setAction(Event.App_Event_WifiConnected.name());
newIntent.putExtra("App_Events_SSID", lastConnectedSsid);
context.sendBroadcast(newIntent);
}
if(state == SupplicantState.DISCONNECTED)
{
boolean wifiEnabled = ((WifiManager)context.getSystemService(Context.WIFI_SERVICE)).isWifiEnabled();
if(wifiEnabled)
{
Intent newIntent = new Intent();
newIntent.setAction(Event.App_Event_WifiDisconnected.name());
newIntent.putExtra("App_Events_SSID", lastConnectedSsid);
context.sendBroadcast(newIntent);
}
}
}
}
}
I got the same problem in some custom roms. I used "android.net.wifi.STATE_CHANGE" to listen the network change. In the receiver, I used "(NetworkInfo)intent.getParcelableExtra("networkInfo")).getState()" to get the network state. There are three states: DISCONNECTED, CONNECTING, CONNECTED. You can use DISCONNECTED to detect if the network is disconnected.
Please let me know if it works in your situation(HTC One X (4.1)).
I need to detect when I have network connectivity to a SPECIFIC WIFI network.
For example: As soon as you walk into your house, and your phone picks up your home WiFi network, I would like a notification that says "You are not at your home network, would you like to connect to you Home?" But I would like that to only happen when I am at my specific house.
What should I listen for and what tests should I do to make sure it is my specific home network, and not another network?
You can use BroadcastReceiver to find out that wifi network has changed:
BroadcastReceiver broadcastReceiver = new WifiBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
context.registerReceiver(broadcastReceiver, intentFilter);
The BroadcastReceiver may look like this.
And to check for specific MAC address see the checkConnectedToDesiredWifi() method bellow.
public class WifiBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION .equals(action)) {
SupplicantState state = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
if (SupplicantState.isValidState(state)
&& state == SupplicantState.COMPLETED) {
boolean connected = checkConnectedToDesiredWifi();
}
}
}
/** Detect you are connected to a specific network. */
private boolean checkConnectedToDesiredWifi() {
boolean connected = false;
String desiredMacAddress = "router mac address";
WifiManager wifiManager =
(WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifi = wifiManager.getConnectionInfo();
if (wifi != null) {
// get current router Mac address
String bssid = wifi.getBSSID();
connected = desiredMacAddress.equals(bssid);
}
return connected;
}
}
As long, as we are nothing like code as you need, for free service, I can only recommend you, to read everything possible about Android and its Network/Wifi possibilities, when creating such app.
Sources you should read up and study
http://developer.android.com/reference/android/net/wifi/package-summary.html
how to see if wifi is connected in android
How to get my wifi hotspot ssid in my current android system
How to get name of wifi-network out of android using android API?
Get Wifi Interface name on Android
Permissions you should ask for when creating application manifest
AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
(the last one, only if you want it to detect your location, to prevent unnecessary calls)
You should also declare, that your application needs wifi to be available in device, to work properly:
AndroidManifest.xml
<uses-feature android:name="android.hardware.wifi" />
Use the standard code to list all available networks:
start the scan
String connectivity_context = Context.WIFI_SERVICE;
final WifiManager wifi = (WifiManager) getSystemService(connectivity_context);
if (wifi.isWifiEnabled()) {
wifi.startScan();
}
register a receiver for the data
IntentFilter i = new IntentFilter();
i.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent i) {
// TODO Auto-generated method stub
ScanWiFiActivity a = ScanWiFiActivity.instance();
WifiManager w = (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);
List<ScanResult> l = w.getScanResults();
a.Clear();
for (ScanResult r : l) {
//use r.SSID or r.BSSID to identify your home network and take action
a.setText(r.SSID + "" + r.level + "\r\n");
}
}
};
registerReceiver(receiver, i);
In the FOR block work your magic and take action when you identify your network by SSID or BSSID
I had exactly the same problem for a project of mine and it took a while to find a solution.
First of all, "connecting to a WiFi" is something very abstract, and it turns out rightly so. In practice, people usually mean all of the following:
authenticated with a WiFi access point
associated with the access point
got an ip address from the network
All these stages (and several more) are associated with different Andoid events. So, without further ado, here is my (slightly modified) code:
public class MyService extends Activity { // or Service
//... Other stuff
BroadcastReceiver awaitIPAddress = null;
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
if (intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE) == SupplicantState.COMPLETED) {
//WiFi is associated
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo wi = wifiManager.getConnectionInfo();
if (wi != null) {
// Wifi info available (should be, we are associated)
if (wi.getIpAddress() != 0) {
// Lucky us, we already have an ip address.
// This happens when a connection is complete, e.g. after rekeying
if (wi.getBSSID().equals("c0:ff:ee:c0:ff:ee")) {
// ... Do your stuff here
// ...
// ...
}
} else {
// No ip address yet, we need to wait...
// Battery friendly method, using events
if (awaitIPAddress == null) {
awaitIPAddress = new BroadcastReceiver() {
#Override
public void onReceive(Context ctx, Intent in) {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo wi = wifiManager.getConnectionInfo();
if (wi != null) {
if (wi.getIpAddress() != 0) {
if (wi.getBSSID().equals("c0:ff:ee:c0:ff:ee")) {
// ... Do your stuff here
// ...
// ...
}
}
} else {
ctx.unregisterReceiver(this);
awaitIPAddress = null;
}
}
};
// We register a new receiver for connectivity events
// (getting a new IP address for example)
context.registerReceiver(awaitIPAddress, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
}
}
} else {
// wifi connection not complete, release ip address receiver if registered
if (awaitIPAddress != null) {
context.unregisterReceiver(awaitIPAddress);
awaitIPAddress = null;
}
}
}
}
};
//... Other stuff
#Override
public void onCreate() {
super.onCreate();
//... Other stuff
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
registerReceiver(receiver, filter);
//... Other stuff
}
//... Other stuff
}
Also, don't neglect the appropriate permissions to the manifest:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
And I strongly suspect you will also need:
<uses-permission android:name="android.permission.INTERNET"/>