Force Android app to connect to a certain WiFi network
I’m building an app that connects to a certain WiFi network that has no internet access, I added the network configuration through the app (not through the settings) so I can have the ability to enable/disable this network. The application works as follows: 1-The user presses a button. 2-I check if WiFi is enabled, if not, I enable it. Also, if the phone is already connected to a WiFi network, I disconnect it from that network and connect to mine(Which works fine). 3-I connect to the network using the following methods(After enabling WiFi) :
mWifiManager.enableNetwork(networkId, true);//I have the network ID from the list of configured networks. mWifiManager.reconnect();
But this doesn’t seem to work if the WiFi was initially disabled, WiFi gets enabled but doesn’t always connect to my network, it sometimes connects to other configured networks if available. I tried giving a high priority to my network(Higher than all other configured networks), but it didn’t make any difference. I tried also enabling the network before enabling the WiFi but it didn’t work. I tried adding a mWifiManager.disconnect(); but no change.
mWifiManager.disonnect(); mWifiManager.enableNetwork(networkId, true); mWifiManager.reconnect();
So I have some questions : Q1-Why does it work when you are already connected to a WiFi network, but not if WiFi is disabled? Q2-Is there any working way to force an app to connect to a specific network?(I’m working on Android 7.0 API 24). Here is my code:
int mID; String mSSID; String mPASSWORD; public void connectToWiFi(String ssid, String key) < mSSID = ssid; mPASSWORD = key; if (!mWifiManager.isWifiEnabled()) < WifiUtils.withContext(mContext).enableWifi(this::enableResult); //Also tried // boolean wifiEnabled = mWifiManager.setWifiEnabled(true); return; >connectToNetwork(); > private void enableResult(boolean b) < //This is to make sure WiFi has been enabled successfully Log.w(TAG, "enableResult:" + b); if (b) < connectToNetwork(); >> private void connectToNetwork() < mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);// also tried to store the id in shared prefs and get it back here (did not work for me) mID = getExistingNetworkId(mSSID); if (mID == -1)/Network has not been configured yet, so I add it mID = addNetworkToConfiguration(); MyApplication.getInstance().getShared().setNetowrkID(mID);// store the id in shred prefs mWifiManager.saveConfiguration(); > boolean disconnect = mWifiManager.disconnect(); boolean result = mWifiManager.enableNetwork(mID, true); boolean reconnect = mWifiManager.reconnect(); > private int addNetworkToConfiguration() < WifiConfiguration config = new WifiConfiguration(); config.SSID = quoted(mSSID); config.status = WifiConfiguration.Status.ENABLED; config.preSharedKey = quoted(mPASSWORD); config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); config.allowedProtocols.set(WifiConfiguration.Protocol.RSN); config.allowedProtocols.set(WifiConfiguration.Protocol.WPA); assignHighestPriority(config); return mWifiManager.addNetwork(config); >private int getExistingNetworkId(String SSID) < mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); ListconfiguredNetworks = mWifiManager.getConfiguredNetworks(); if (configuredNetworks != null) < for (WifiConfiguration existingConfig : configuredNetworks) < if (areEqual(trimQuotes(existingConfig.SSID), trimQuotes(SSID))) < return existingConfig.networkId; >> >else < Log.e(TAG,"Unable to fetch network configurations"); >return -1;> private void assignHighestPriority(WifiConfiguration config) < ListconfiguredNetworks = mWifiManager.getConfiguredNetworks(); if (configuredNetworks != null) < for (WifiConfiguration existingConfig : configuredNetworks) < if (config.priority > > >
Android WiFi connection programmatically
Do you have any idea how to establish a wifi connection with sending password in my android application?
4 Answers 4
Pass the SSID and it’s password to the following method.
public void connectToAP(String ssid, String passkey) < Log.i(TAG, "* connectToAP"); WifiConfiguration wifiConfiguration = new WifiConfiguration(); String networkSSID = ssid; String networkPass = passkey; Log.d(TAG, "# password " + networkPass); for (ScanResult result : scanResultList) < if (result.SSID.equals(networkSSID)) < String securityMode = getScanResultSecurity(result); if (securityMode.equalsIgnoreCase("OPEN")) < wifiConfiguration.SSID = "\"" + networkSSID + "\""; wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); int res = wifiManager.addNetwork(wifiConfiguration); Log.d(TAG, "# add Network returned " + res); boolean b = wifiManager.enableNetwork(res, true); Log.d(TAG, "# enableNetwork returned " + b); wifiManager.setWifiEnabled(true); >else if (securityMode.equalsIgnoreCase("WEP")) < wifiConfiguration.SSID = "\"" + networkSSID + "\""; wifiConfiguration.wepKeys[0] = "\"" + networkPass + "\""; wifiConfiguration.wepTxKeyIndex = 0; wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40); int res = wifiManager.addNetwork(wifiConfiguration); Log.d(TAG, "### 1 ### add Network returned " + res); boolean b = wifiManager.enableNetwork(res, true); Log.d(TAG, "# enableNetwork returned " + b); wifiManager.setWifiEnabled(true); >wifiConfiguration.SSID = "\"" + networkSSID + "\""; wifiConfiguration.preSharedKey = "\"" + networkPass + "\""; wifiConfiguration.hiddenSSID = true; wifiConfiguration.status = WifiConfiguration.Status.ENABLED; wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.WPA); int res = wifiManager.addNetwork(wifiConfiguration); Log.d(TAG, "### 2 ### add Network returned " + res); wifiManager.enableNetwork(res, true); boolean changeHappen = wifiManager.saveConfiguration(); if(res != -1 && changeHappen)< Log.d(TAG, "### Change happen"); AppStaticVar.connectedSsidName = networkSSID; >else < Log.d(TAG, "*** Change NOT happen"); >wifiManager.setWifiEnabled(true); > > > public String getScanResultSecurity(ScanResult scanResult) < Log.i(TAG, "* getScanResultSecurity"); final String cap = scanResult.capabilities; final String[] securityModes = < "WEP", "PSK", "EAP" >; for (int i = securityModes.length - 1; i >= 0; i--) < if (cap.contains(securityModes[i])) < return securityModes[i]; >> return "OPEN"; >
Don’t forget to add necessary permission in the Manifest file.
Connect to Wifi in Android Q programmatically
I had this function to connect in Wifi network, below Android 10 it works fine, but when I tried on Android 10, I had a successful connection but WITHOUT internet, I knew it’s a bug in Android 10 but I found this application which can connect to wifi from Android 10 with no problem. I’m blocked for days. My function :
private void connectToWifi(String ssid, String password) < WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) < try < Log.e(TAG,"connection wifi pre Q"); WifiConfiguration wifiConfig = new WifiConfiguration(); wifiConfig.SSID = "\"" + ssid + "\""; wifiConfig.preSharedKey = "\"" + password + "\""; int netId = wifiManager.addNetwork(wifiConfig); wifiManager.disconnect(); wifiManager.enableNetwork(netId, true); wifiManager.reconnect(); >catch ( Exception e) < e.printStackTrace(); >> else < Log.e(TAG,"connection wifi Q"); WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier.Builder() .setSsid( ssid ) .setWpa2Passphrase(password) .build(); NetworkRequest networkRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .setNetworkSpecifier(wifiNetworkSpecifier) .build(); connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); networkCallback = new ConnectivityManager.NetworkCallback() < @Override public void onAvailable(Network network) < super.onAvailable(network); connectivityManager.bindProcessToNetwork(network); Log.e(TAG,"onAvailable"); >@Override public void onLosing(@NonNull Network network, int maxMsToLive) < super.onLosing(network, maxMsToLive); Log.e(TAG,"onLosing"); >@Override public void onLost(Network network) < super.onLost(network); Log.e(TAG, "losing active connection"); >@Override public void onUnavailable() < super.onUnavailable(); Log.e(TAG,"onUnavailable"); >>; connectivityManager.requestNetwork(networkRequest,networkCallback); > >
@euphor but that won’t work after 2nd Nov deadline. We can’t push update to play store after 2nd Nov. Any other workaround?
8 Answers 8
So far what is working for me on the majority of devices I have tested with, with a fallback option to at least stop the dreaded ‘looping request’ and to allow a successful manual connection
The below code is written in Kotlin, please google how to covert to Java if needed.
Create a NetworkCallback which is required for API >= 29 (prior it was not required but could be used)
val networkCallback = object : ConnectivityManager.NetworkCallback() < override fun onAvailable(network: Network) < super.onAvailable(network) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) < // To make sure that requests don't go over mobile data connectivityManager.bindProcessToNetwork(network) >else < connectivityManager.setProcessDefaultNetwork(network) >> override fun onLost(network: Network) < super.onLost(network) // This is to stop the looping request for OnePlus & Xiaomi models connectivityManager.bindProcessToNetwork(null) connectivityManager.unregisterNetworkCallback(networkCallback) // Here you can have a fallback option to show a 'Please connect manually' page with an Intent to the Wifi settings >>
Connect to a network as follows:
val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder() .setSsid(ssid) .setWpa2Passphrase(pass) .build() val networkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) // Add the below 2 lines if the network should have internet capabilities. // Adding/removing other capabilities has made no known difference so far // .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) // .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) .setNetworkSpecifier(wifiNetworkSpecifier) .build() connectivityManager.requestNetwork(networkRequest, networkCallback)
As stated here by Google, some OEM Roms are not ‘holding on to the request’ and therefore the connection is dropping instantly. OnePlus have fixed this problem in some of their later models but not all. This bug will continuously exist for certain phone models on certain Android builds, therefore a successful fallback (i.e. a manual connection with no network disruption) is required. No known workaround is available, but if found I will update it here as an option.
To remove the network, do the following:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) < //This is required for Xiaomi models for disconnecting connectivityManager.bindProcessToNetwork(null) >else < connectivityManager.setProcessDefaultNetwork(null) >connectivityManager.unregisterNetworkCallback(it)
Please keep in mind, an automatic connection allows for an automatic & manual disconnection. A manual connection (such as the suggested fallback for OnePlus devices) does not allow an automatic disconnection. This will also need to be handled within the app for a better UX design when it comes to IoT devices.
Some extra small tips & info:
- now that a system dialog opens, the app calls onPause and onResume respectively. This affected my logic regarding automatic connection to IoT devices. In some case, onResume is called before the network callback is finished.
- In regards to tests, I have yet to be able to get around the dialog by just using espresso and it may block some tests that were working before API 29. It may be possible using other frameworks such as uiautomator. In my case I adjusted the tests to work up until the dialog shows, and run further tests thereafter. Using Intents.init() does not work.
- onUnavailable is called when the the network has been found, but the user cancels. It is not called when the network was not found or if the user cancels the dialog before the network has been found, in this case no other methods are called, use onResume to catch it.
- when it fails on the OnePlus it called onAvailable() -> onCapabilitiesChanged() -> onBlockedStatusChanged (blocked: false) -> onCapabilitiesChanged() -> onLost() respectively
- removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) wont help keep the connection on a OnePlus as stated here
- setting the Bssid wont help keep the connection on a OnePlus as stated here
- google cannot help, they have stated it is out of their hands here
- OnePlus forum posts confirming it working for some models (but not all) after an update, see here, here & here
- when GPS is switched off, the SSID names of networks are not available
- if the dialog comes several times, check your own activity lifecycle, in my case some models were calling onResume before the network callback was received.
- manually connecting to a network without internet capabilities needs user confirmation to keep the connection (sometimes in the form of a dialog or as a notification), if ignored, the system will disconnect from the network shortly afterwards
- Google Pixel 2 — No issues found
- Samsung S10 SM-G970F — No issues found
- Samsung S9 SM-G960F — No issues found
- One Plus A5000 (OxegenOS 10.0.1) — Major Issue with automatic connection
- HTC One M8 (LineageOS 17.1) — No issues found
- Xiaomi Mi Note 10 — Issue with disconnecting (Fixed, see code example)
- Samsung A50 — Dialog repetitively appears after successful connection (sometimes)
- Huawei Mate Pro 20 — Dialog repetitively appears after successful connection (sometimes)
- Huawei P40 Lite — Doesn’t call onLost()
- CAT S62 Pro — No issues found
- Sony Xperia SZ2 — No issues found
- Samsung Note10 — No issues found