I would like to connect to WiFi network programmatically.
Here is my code:
wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(true);
WifiConfiguration config = new WifiConfiguration();
config.SSID = "\"" + ssid + "\"";
config.preSharedKey = "\""+ key +"\"";
int netId = wifiManager.addNetwork(config);
wifiManager.saveConfiguration();
wifiManager.disconnect();
wifiManager.enableNetwork(netId, true);
wifiManager.reconnect();
When I have wifi enabled on my phone, it works as expected, but the problem is, when wifi is disabled. In this case the only result is enabling wifi adapter, but not connecting to the network. It seems like enabling takes to long so it won't get connected. Another strange thing to me is that wifiManager.getConfiguredNetworks() returns null. Do you know how to fix that?
Thank you
It seems like enabling takes to long so it won't get connected.
Yes. This is because enabling of the network is done async, it happens in parallel, and doesn't occur immediately. Here are two possible solutions to your problem:
1) This is the easiest solution, but not the best. Loop as described by another user checking for the scan results to come in. However, you should add a sleep of some sort between every cycle of the loop. I.E. you want to wait for 1ms, so as to not eat up all the CPU resources. I am not sure how to do this in Android off the top of my head. There is another problem with this method. If u are in the GUI thread, you will block all GUI events this way, as you wait for the connection to be established.
2) This is the proper solution. You can register for broadcast events after the network connection has been established. Then you will get an event when it finishes. From this event you can finish performing whatever operations are needed.
Sorry for the rushed answer. I am not an Android pro, so I can't explain the details as to how to do this off the top of my head, but I thought I would at least point you in the right direction.
Actually if you connect WiFi more than one time it will solve your issue.
One other thing I see if my WiFi is enabled and I connect to a specific WiFi network then it's working.
One other thing when I switch from mobile network to a specific WiFi network then it gives an unstable connection ..for this problem I connect WiFi through a specific WiFi network and then forget the network after a 3 second delay I again connect. Then it works properly.
I use this code for connecting to WiFi.
And for delay and for got WiFi network I use this code... .......
{
wifi(SSID,PASS);
final Handler handler = new Handler();
handler.postDelayed(
new Runnable()
{
Override public void run() { forgot(); }
}, 3000);
final Handler handler1 = new Handler();
handler1.postDelayed(
new Runnable()
{
Override public void run() {wifi(SSID,PASS); }
}, 3000);
}
Related
I have a deployed app that is failing on Android 9. Part of its function is to configure a module over an Access Point network to allow that that module to connect to the users home network.
I have code that detects and connects to the correct WIFI network, but when I attempt to open a socket to the device, it fails - only on Android 9 and only if mobile data is enabled. If I manually disable mobile data on the device everything runs fine.
Socket open() {
Socket sock = new Socket(Proxy.NO_PROXY);
try {
sock.bind(new InetSocketAddress(localIpAddress(), 50000));
} catch (IOException e) {
activity.logContent("Warning: Failed to bind socket : " + e.toString());
}
try {
sock.connect(new InetSocketAddress("192.168.17.1", 5555), (int)5000);
} catch (IOException e) {
// This catch fires when Mobile Data is on.
activity.logContent("Connected to " + activity.mWifiManager.getConnectionInfo().getSSID());
activity.logContent("Couldn't open socket : " + e.toString());
}
return sock;
}
I have tried this with and without the Proxy.NO_PROXY and with and without the bind() call. If the bind call is missing the error implies that the socket is attempting to connect over the cell network. (Note: activity.logContent() is an on-screen log so it is easier to see what is happening when not connected to a debugger).
Any ideas what is going wrong?
After a few days of imprecations I believe I have come to the identification of the problem and therefore to the solution:
The problem occurs due to some changes in the version of android (I presume to be 9.0 even if other changes had occurred on API 21), in particular on the creation of the socket, if the system detects that there is a "better" network (access to internet, high signal, etc, etc) socket creation refers to that network and no longer to the wifi network you would like.
I looked for ways to force the creation of the socket on the wifi network (which is the network I want) and the only way I found is this:
Simply put instead of:
Socket sock = new Socket ();
Do:
ConnectivityManager connectivity = (ConnectivityManager) MyApp.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null)
{
for (Network network : connectivity.getAllNetworks())
{
NetworkInfo networkInfo = connectivity.getNetworkInfo(network);
if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI)
{
if (networkInfo.isConnected())
{
Socket sock = network.getSocketFactory().createSocket();
}
}
}
}
Practically browse the networks present in the device and when you find your active wifi you do nothing but take advantage of this function to get the right socket for sure:
getSocketFactory().createSocket()
Now you have the working socket!
In my case it now works perfectly, if someone finds better solutions, it is welcome, but for now it is the only way I have found to make everything work as in the previous version of android.
In Android 9 there a security config about network: Android security config
Adding your domain in network_security_config might solve your problem. I had this in my network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">119.xxx.xxx.xxx</domain>
</domain-config>
</network-security-config>
I am not sure about the exact reason of why this is happening. However, when you are turning on your mobile data and you are only connected to the internet using your mobile data (considering your wifi is turned off), it gets the IP address from the cellular network which is no more connected in your home network. Hence, this is trivial to expect such timeout scenarios, because, it cannot reach the private IP addresses of your home network starting with 192.168.....
Now my confusion is that even if the mobile data is turned on, and both wifi and mobile data is turned on at the same time, the device should connect to the wifi as a default behavior.
Hence I would like to suggest you check the following.
Android 9 (Pie) introduces special Wifi preference, which prevents connecting to public networks automatically. You might consider checking the settings.
Please check the IP address of your device and check if it has some IP address starting with 192.168..... If not, then definitely, you are getting your IP address from your cellular network and hence it cannot reach your private IP addresses of the home network.
We have an app we are making that needs to switch to cellular for some requests even when WiFi is connected.
According to the ConnectionManager documentation these following methods are now deprecated, but is not so clear on what to use instead.
public void useMobileNetworkMode(Context context) {
ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
cm.setNetworkPreference(ConnectivityManager.TYPE_MOBILE);
}
public void useDefaultNetworkMode(Context context) {
ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
cm.setNetworkPreference(ConnectivityManager.DEFAULT_NETWORK_PREFERENCE);
}
Do these methods still work in android 5/6? and if anyone has info or something to replace these methods with I would be very grateful!
Ok little bit more on the problem :)
I have managed to investigate ConnectivityManager and can see the networks using :
Network networkToUse = null;
Network[] networks;
networks = cm.getAllNetworks();
for (Network network : networks) {
NetworkInfo ni = cm.getNetworkInfo(network);
Log.e("NETWORKINFO", ni.getType() + " " + ni.getExtraInfo());
if (ni.getType()== ConnectivityManager.TYPE_WIFI) {
Log.e("NETWORKINFO", "isWifi");
if (ni.isConnected()) {
Log.e("NETWORKINFO", "and is connected");
if (networkToUse == null) {
networkToUse = network;
}
}
}
if (ni.getType()== ConnectivityManager.TYPE_MOBILE) {
Log.e("NETWORKINFO", "HasMobile");
if (ni.isConnected()) {
Log.e("NETWORKINFO", "and is connected");
networkToUse = network;
}
}
}
It is here I kind of get stuck because I can't logically see a way of telling the app to use one of these networks when performing a HttpsURLConnection from URL.openLink();.
UPDATE:
I have just noticed that the mobile one disappears shortly after WiFi connects. There is a moment where I get both but not for long.
I have also tried this:
final ConnectivityManager connection_manager =
(ConnectivityManager)httpsClient.getCheckoutController().getCheckout().getCurrentActivity().getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
connection_manager.registerNetworkCallback(request.build(), new ConnectivityManager.NetworkCallback() {
#Override
public void onAvailable(Network network) {
Log.e("NETWORKINFO", "FOUND A CELLULAR NETWORK " + connection_manager.getNetworkInfo(network));
}
});
request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
connection_manager.registerNetworkCallback(request.build(), new ConnectivityManager.NetworkCallback() {
#Override
public void onAvailable(Network network) {
Log.e("NETWORKINFO", "FOUND A WIFI NETWORK "+connection_manager.getNetworkInfo(network));
}
});
but as with the "list" of networks" in previous try I only ever 1 callback, even if mobile data is on as well.
UPDATE;
Ok I seem to see mobile sometimes using above method. but it seems to create API level spaghetti hell. It sometimes also takes a very long time for the mobile callback to fire. I wonder if this is because it has to wake up the cellular modem and wait for it's handshake or something?
UPDATE;
I had another possible suggestion using Sockets (something I have little experience with..)
Does anyone know if it is possible to build a CELLULAR SSL connection socket to do https requests using HttpsURLConnection.getSocketFactory() and SSLSocket ?
Any info would be very welcome in this week long quest :D
UPDATE:
Found a good and categorical answer from someone at google:
How to stay connected through mobile network after WIFI is connected on Android?
However after implementation, i get a network callback for the mobile but when i open URL connection and perform a request it seems to get stuck for ages (about 4 minutes) before i get the response.
I have a Huawei 5.0.1 phone, which is the highest i have available. Obviously this is not good. However it is not tested on 5.1 galaxy S6 and works.. so could be the phone.
According to the Android documentation it is no longer working in Android version 5 and above.
This method was deprecated in API level 21.
Functionality has been removed as it no longer makes sense, with many more >than two networks - we'd need an array to express preference. Instead we >use dynamic network properties of the networks to describe their >precedence.
Found a good and categorical answer from someone at google: How to stay connected through mobile network after WIFI is connected on Android?
(Link is in edited info above)
However after implementation, i get a network callback for the mobile but when i open URL connection and perform a request it seems to get stuck for ages (about 4 minutes) before i get the response.
I have a Huawei 5.0.1 phone, which is the highest i have available. Obviously this is not good. However it is not tested on 5.1 galaxy S6 and works.. so could be the phone.
I'm programming a game in Android that uses AI, which requires big CPU power, that a normal Android device just doesn't have. So I decided to write a server in Java using sockets that will calculate everything and return a value to the client (the android device).
Now, I'm used to program for PC, but not for phones. In mobile, the IP of the device can change back and forth due to data roaming and WIFI.
My question is, how do you handle a changing IP? How do you tell a new connection is the same device? Or maybe the Android device does all of that automatically?
I'm new to stackoverflow, I hope I didn't ask too many questions. :)
Thank you very much for your answers!
You don't need to handle ip changing at all. A client(an android device) must know server host/ip and reconnect if it was disconnected from network, nothing more.
private static class ConnectionTask implements Runnable {
private boolean connected;
#Override public void run() {
try {
InetAddress serverAddress = InetAddress.getByName("host");
Socket socket = new Socket(serverAddress, 9999);
connected = true;
while (connected) {
// sending or writing data
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
//here you lost the connection due to some reason
//you need to notify user about the problem and wait for connection
}
}
}
To receive event about network state you need to register receiver:
context.registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (isNetworkAvailable()) {
unregisterReceiver(this);
tryToConnect();
}
}
}, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
Okay guys I figured out a way, thank to Fox in socks' answer.
Each time a user connects to the server socket, you take his UUID (or an hashed version of it, if you want more security :P ).
Then, when that user disconnects for some reason and tries to connect to the server socket again, he'll send the same UUID. That way, you can tell both of the connections are the same, and continue with the processing.
For more information about UUID, look here:
Is there a unique Android device ID?
Thank you all! :)
Now how do I mark this question as a closed one? :P
I need to disable the Bluetooth and the enable it again. I disable that at this way:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.disable();
}
but I can not enable that again at this way(nothing happen after running this code):
mBluetoothAdapter.enable();
why?
As this involves tinkering with actual hardware, I think you have to wait a bit before being able to enable it. You could try and see with Thread.sleep(500) or 1000 before enabling...
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.disable();
Thread.sleep(500); //code for dealing with InterruptedException not shown
mBluetoothAdapter.enable();
}
However, this is flaky, having magic numbers resolve issues related to events of a hardware device is not nice. Not guarranteed to have well defined behaviour under all circumstances (different devices, different situations, etc...)
If the adapter reports disabled state only when it has been fully disabled, you could try something along these lines:
int retry=0;
while(retry++<5) {
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable();
}
Thread.sleep(100); //again, InterruptedException handling not shown
}
if(retry==5) {
//Ooops, still not successful. Handle situation here.
}
So in words: smaller delay, but a few retries, and check if it has been disabled or not.
This happens if you transfer data via Bluetooth Socket. One socket - use 1 port. You can enable\disable bluetooth whlie all sockets ports(around 30) not be filled. So a question - how to get around of this problem, so a answer - keep track of close\open sockets. May be it helps.
In Juice, the enum DetailedState adds a new state named
/** Link has poor connectivity. */
VERIFYING_POOR_LINK
but what is this state stand for?
Having searched for the entire project, I found this:
The subclass VerifyingLinkState in WifiStateMachine.java
class VerifyingLinkState extends State {
#Override
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
sendNetworkStateChangeBroadcast(mLastBssid);
}
#Override
public boolean processMessage(Message message) {
switch (message.what) {
case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
//stay here
break;
case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
try {
mNwService.enableIpv6(mInterfaceName);
} catch (RemoteException re) {
loge("Failed to enable IPv6: " + re);
} catch (IllegalStateException e) {
loge("Failed to enable IPv6: " + e);
}
setNetworkDetailedState(DetailedState.CONNECTED);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mConnectedState);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
When it is verifying link state, in the enter() function, it sets the DetailedState to
DetailedState.VERIFYING_POOR_LINK
which will cause the user being confused for getting a state message like the following picture while the connection is really good.
Although this message only stays for a while and then replaced by "Connected" swiftly. But what is this state aim for? what are the risk if I don't set the DetailedState to VERIFYING_POOR_LINK in the enter() function.
VERIFYING_POOR_LINK for certain Samsung Devices are a part of the Smart Network Switch in Wi-Fi which can allow Mobile Data to remain connected over Wi-Fi if poor conditions occur. For Samsung Devices, that's when you would see a detailed state go from CONNECTED to VERIFYING_POOR_LINK.
Most of the logic can be found in: http://androidxref.com/4.3_r2.1/xref/frameworks/base/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
WifiWatchdogStateMachine monitors the connection to a WiFi network. When WiFi
connects at L2 layer, the beacons from access point reach the device and it
can maintain a connection, but the application connectivity can be flaky (due
to bigger packet size exchange).
We now monitor the quality of the last hop on WiFi using packet loss ratio as
an indicator to decide if the link is good enough to switch to Wi-Fi as the uplink.
When WiFi is connected, the WiFi watchdog keeps sampling the RSSI and the
instant packet loss, and record it as per-AP loss-to-rssi statistics. When
the instant packet loss is higher than a threshold, the WiFi watchdog sends a
poor link notification to avoid WiFi connection temporarily.
While WiFi is being avoided, the WiFi watchdog keep watching the RSSI to
bring the WiFi connection back. Once the RSSI is high enough to achieve a
lower packet loss, a good link detection is sent such that the WiFi
connection become available again.
BSSID roaming has been taken into account. When user is moving across
multiple APs, the WiFi watchdog will detect that and keep watching the
currently connected AP.
Power impact should be minimal since much of the measurement relies on passive statistics already being tracked at the driver and the polling is done when screen is turned on and the RSSI is in a certain range.
Although I can't give a for sure (or guaranteed) answer..
I would say that it means that the connection is weak (Less than 30%(?) signal)
Edit:
Android offers the ability to avoid/ignore low-connectivity access points - I'm assuming that this state is suggesting that the connection shouldn't be used.
I don't see any risks in ignoring/neglecting this state.
Edit 2:
In light of recent comments, I have taken this excerpt from the android documentation:
public static final NetworkInfo.DetailedState VERIFYING_POOR_LINK
Added in API level 16 Link has poor connectivity.
It can be found at NetworkInfo.DetailedState