To my understanding, one can request the WifiManager to start an AP scan, which is great and you get scan results back, but my question is regarding the continuous wifi scan that happens under the hood.
Aside from unregistering the wifi listener for scan callback, is disabling the wifi the only way to stop the hardware from scanning? So if my device is connected to some Wifi BSSID/SSID it's ALSO constantly scanning? (Yes http://www.androidauthority.com/community/threads/how-to-get-wifi-to-stop-scanning-after-connected.7760/) So if I request a scan while connected to some Wifi, it starts to constantly scan since there's no way to actually stop the scanning without turning off the hardware... at which point you lose connectivity with the Wifi Network, NOT acceptable.
Seems like an oversight from Google. Why didn't they just leave the hardware alone rather than make it constantly scan?! Unless provoked to start scanning, connect, disconnect, or stop scanning, DO NOT DO ANYTHING on your own... why didn't they implement it this way? My concern is battery drainage with continuous scanning... whether or not I have a listener is irrelevant. The fact that the hardware is constantly querying for nearby networks sounds pretty resource and battery intensive.
Is there anything one can do in this case without rooting?
Related
I am using the Nearby Connections API. From what I've read, one should not be discovering while simultaneously being connected to a device because this reduces bandwith and causes possible dropped connections.
However, I am building an application where each phone tries to relay any message it receives to as many other phones as possible. As such, it is best to always be discovering in order to maximizing the number of endpoints.
What is the best method for ensuring that discovery time does not overlap with actual connection time? Should I be entering discovery mode on a regular preset interval? Is there a way to store discovered devices for later (so I can connect to them after I have discovering all local devices)?
You can connect to discovered devices even after calling stopDiscovery, so one solution is to scan for a fixed duration, and then connect to the devices that were found. (Note: If you're trying to do something in the background, try to synchronize the devices by advertising on the start of the hour since discovery can drain a lot of battery.)
Alternatively, you can try to keep the devices continuously connected to each other, by forming 2~3 connections per device, and flooding the network by updating all of your connected peers, who then do the same, until everyone sees the message. (Note: Being connected drains battery, although not as much as discovery does)
My goal is to get the Android device to reconnect to a BLE device that it has previously connected to without user intervention in the same way it does for a classic BT paired device does (even works through power cycles).
One of the ideas of BTLE devices is that one saves service, bonding, and enabling states such that a reconnect is VERY fast and consumes very little power on the peripheral.
What I have done seems to work but it works poorly.
The first step is to connect or pair and connect to a new device setting the 'autoconnect' parameter to 'true'.
When the device disconnects, do not call gatt.close(). Everywhere I look I see that one should call gatt.close(). But if I do call gatt.close() the Android central app never reconnects. I have tested this many times.
If I have not called gatt.close() and have not power cycled the Android, the auto-connection usually happens. Sometimes it can take a long time, especially after version 5.0. It is, however, unreliable and it may be unreliable due to a very low-duty scan cycle and the device quitting advertising before a scan cycle actually detects the advertisement. I am not sure because there is no way to detect the scanning operation like there is advertisements! It is also possible the scanning stops after a certain amount of time but there is no documentation on that.
So what I think I need to do is to somehow set the background scan rate used by the Android to a higher duty cycle (only possible in 5.0 and up) when auto-connect has been set but I do not know how to do this. I do not want to start my own scan but somehow set the background scanning rate used by Android for the reconnect. Does anyone know how to do this? Does anyone really know how autoconnect and gatt.close() are to work?
Maybe the auto-connect was NOT meant to re-connect as I indicated above?
Well after many trials and tribulations this is how I best get the Android to auto connect with the only user action being to first select the device (if using the settings menu then first pairing).
You have to trap the paired event in a BroadcastReceiver and do a BluetoothDevice.connectGatt() setting the autoconnect to true. Then when the device disconnects, invoke a gatt.connect().
Update: While the above approach works in general, it is sometimes agonizingly slow probably because the pending connection uses extremely conservative scan rates. The other downside is that for each device you want to auto-reconnect to you have to keep a BluetoothGatt object performing a pending connection. In the embedded world this is insane. Instead what one does is continuously scan and connect to a desired device by inspecting its advertisement. One saves only the minimal amount of data about the device (the services, its paired state and keys, etc.). When an advertisement is captured you see if it is one of your known devices and connect to if it is.
I tried the equivalent on Android. Scan all the time (low power rate) and connect to advertisements of interest, and maintain a class representing a known device. There are some annoying details in this approach (like turning off scanning while connecting and restarting after connected) but it basically works without the overhead of maintaining connections. BUT there is one exception I do not understand. One pre-paired device's advertisements are never seen by the scanner. However, if I invoke a pending connection to this device, I re-connect. I do not understand this at all. On my embedded platforms it works as it should.
If anyone else has tried this approach for auto-reconnecting, please share your experiences!
I have discovered the reason the pre-paired device is not seen by Android. Android only reports scan results IF the device responds to a scan request. Once paired, this device only emits advertisements and ignores scan requests, so the Android system does not pass up its advertisements in the ScanCallback. Thus in order to work using the scan approach, I have to use the pending connect approach for those specific devices. It just seems like you can't win!
============= UPDATE 2020
Many years have passed and I have a lot more experience with the background scan approach. If one keeps the supported platforms 5 and up, one can use only the newest scanner APIs and use filters, eliminating the need to decode the raw advertisements yourself. I have also found that connection and re-connection is snappier if you DONT turn off scanning while connecting. I know it goes against all documentation, but it works and on some platforms allowed connections to happen that otherwise did not. Also, to date, I have found only one (health) device that needs pending connects. Disclaimer: All I have ever worked with is health devices.
This is how I was able to do it for my application.
I first stored the address of the device in a SharedPreference then in gattClientCallback funtion of my BluetoothLeService
else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_DISCONNECTED;
DeviceActivity.runOnUI(() -> {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String name_dev_1 = sharedPreferences.getString("Dev_1", null) ;
connectToDevice(name_dev_1);
disconnectGattServer();
});
}
}
What this does is if your device is disconnected it will keep on trying to connect to it until a connection is established.
I've been doing some research into this feature for a potential app. I want to give the user the option to turn off WiFi (automatically) when they are getting no signal from a specific network, or perhaps when no data has been transmitted over the connected network for a specific amount of time.
I've seen a promising method in the WiFiManager object, called getScanResults, which returns some information regarding each network, one of which is the signal strength or RSSI, so I would assume I could get the signal strength for the network from this, and manage the currently connected network to detect when it's below a certain level.
However, I've done a small amount of research into this feature, and some applications seem to do it based on triangulation using GPS/mobile data. Is this because it's more accurate?
Is my original idea feasible, using the WiFi manager (or any other Android libraries), can you control WiFi usage based on the signal strength of a network (i.e. its gone below 0), or the network activity (0 for 10 minutes, etc.)
Thanks!
A service can be used to achieve the objectives stated above. The service can have a timer when not connected, a request can be made to the manager on the service which would get all the information from a broadcast receiver.
Services
Broadcast Receiver
This page can be used tofigure out how to find if wifi is connected, I know it is to do withWifiP2p however it is very similar!
In my Android application, it is
noticed that when the device goes to sleep/standby WiFi is
disconnected. When the device wakes up, it gets reconnected. Before making a
httpClient.execute(..) call to remote server we check if the device is connected to n/w.
When the data transfer is being done and if
the device goes to sleep then Android runtime will switch to another
medium for connectivity(3G,GPRS etc.).
Is the switch from WiFi to alternate cellular service
say 3G, seamless?
How do I wait for WiFi to become available again? Should I use Thread.sleep(delay) when the WiFi wakes up? I have seen broadcast actions when the WiFi state changes.
In general, what is the ideal approach to handling WiFi disconnects in a mobile app?
Why not use WifiManager.WifiLock when the transfer is happening and release it when you have finished.
I would suspect network connection switch would not be seamless. I don't know for sure.
A BroadcastReceiver will let you know when Wifi connection state changes. Have a look at ConnectivityManager though because that will monitor Wifi and GPRS etc and it does do failover. Whether it is seamless though I don't know.
By default, Wifi sleep policy is "Sleep on screen idle".
With this policy, is it possible for a Background Service at a later time to wake up Wifi using some API?
Am trying the following, but does not work:
When my Background Service wakes up, it calls "ConnectivityManager.getActiveNetworkInfo()" to get active network.
Since, the wireless is off on idle, I tried waking it up using "WifiManager.startScan" on a previously used Wifi connection.
But still dont get Wifi connectivity.
Any ideas?
I preferably do not want to change my sleep policy to "Never".
Thanks
Hemant
There are no real simple solutions for this. To with a high probability ensure you have WIFI connectivity when the phone/screen goes to sleep the best way is to turn it off. Look here for a lot of details - http://wififixer.wordpress.com/
It is important to realize that in sleep mode the Wifi enters a low power mode. This will become tricky then to programmatically check as it might have connectivity to the Wifi but the Wifi connection is too weak or too slow to complete the HTTP request and hence it times out. This would force you to also check the speed of the Wifi connectivity as well as you will have an active network but a pretty lousy one.
Proper handling of the escaping when timeout occurs for the HTTP call you make makes it ok to use but ultimately the only way to have a background thread constantly running to get data is only doable when you have the Wifi mode to never sleep.
It is tricky and not the best way I know. :-( It is however the only path I have found which is reliable enough.