I need to perform Wifi scans at regular intervals. I am encountering a problem when the time interval is set to 1-2 seconds. It seems like I am not getting any ScanResult. Is there a minimum amount of time to set so that the WifiManager is able to perform a successful WiFi scan?
Here is the code. I am using a Service to do the Wifi scan:
public class WifiScanning extends Service{
private static final String TAG = "WifiScanning";
private Timer timer;
public int refreshRate, numberOfWifiScan, wifiScanGranularity;
WifiReceiver receiverWifi = new WifiReceiver();
WifiManager wifi;
StringBuilder sb;
List<ScanResult> wifiList;
List<APData> apdataList;
List<List<APData>>surveyData;
private TimerTask updateTask = new TimerTask() {
#Override
public void run() {
Log.i(TAG, "Timer task doing work");
wifi.startScan();
}
};
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service creating");
//retrieve the mapRefreshRate from config.xml
XMLOperations test = new XMLOperations();
Configuration config = new Configuration();
config = test.saxXmlParsing(this, 1);
if(config==null)
config = test.saxXmlParsing(this, 2);
refreshRate = Integer.parseInt(config.getMapRefreshRate());
numberOfWifiScan = Integer.parseInt(config.getNumberOfWifiScan_Positioning());
wifiScanGranularity = Integer.parseInt(config.getWifiScanGranularity_Positioning());
timer = new Timer();
Log.i(TAG, "Refresh Rate: "+ String.valueOf(refreshRate));
timer.schedule(updateTask, 0, refreshRate);
wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
registerReceiver(receiverWifi, new IntentFilter(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service destroying");
unregisterReceiver(receiverWifi);
if (timer != null){
timer.cancel();
timer.purge();
timer = null;
}
}
class WifiReceiver extends BroadcastReceiver {
public void onReceive(Context c, Intent intent) {
sb = new StringBuilder();
wifiList = wifi.getScanResults();
String ap_ssid;
String ap_mac;
Double ap_rssi;
for(int i = 0; i < wifiList.size(); i++){
ap_ssid = wifiList.get(i).SSID;
ap_mac = wifiList.get(i).BSSID;
ap_rssi = Double.valueOf(wifiList.get(i).level);
APData ap = new APData(ap_ssid,ap_mac,ap_rssi);
apdataList.add(ap);
sb.append(" " + (wifiList.get(i).SSID).toString());
sb.append(" " + (wifiList.get(i).BSSID).toString());
sb.append((" " + String.valueOf(wifiList.get(i).level)));
sb.append("\n");
}
Log.d(TAG, sb.toString());
for(int i=1; i<=numberOfWifiScan; i++){
surveyData.add(apdataList);
}
}
}
}
However, I seem to get Nullpointer at this line: apdataList.add(ap);. So I wonder whether the interval is too short, which causes ScanResult to be empty?
EDIT after you posted your code:
apdataList does not seem to be initialized in onCreate()
add this to onCreate():
apdataList = new List<APData>();
Minimum scanning delay
I think that there is no absolute minimum scanning delay. It depends too much on the hardware performances.
My advice is that you add a 'As Fast As Possible' option to your preferences then use an asynchronous loop that relaunch a scan as soon as new results are found (see the code snippet below, it was updated to suit your needs). This way, it will only be limited by hardware performances.
Also you can poll the ScanResults using WifiManager.getScanResults() The recommended way is to launch WifiManager.startScan() and set up a BroadcastReceiver for WifiManager.SCAN_RESULTS_AVAILABLE_ACTION to be notified as soon as the scan results are ready.
Here's a sample code (borrowed from here and adapted to your needs):
IntentFilter i = new IntentFilter();
i.addAction (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(new BroadcastReceiver(){
public void onReceive(Context c, Intent i){
// Code to execute when SCAN_RESULTS_AVAILABLE_ACTION event occurs
WifiManager w = (WifiManager) c.getApplicationContext().getSystemService(Context.WIFI_SERVICE); //Use getApplicationContext to prevent memory leak
myScanResultHandler(w.getScanResults()); // your method to handle Scan results
if (ScanAsFastAsPossible) w.startScan(); // relaunch scan immediately
else { /* Schedule the scan to be run later here */}
}
}, i );
// Launch wifiscanner the first time here (it will call the broadcast receiver above)
WifiManager wm = (WifiManager)getApplicationContext.getSystemService(Context.WIFI_SERVICE);
boolean a = wm.startScan();
From Android 8 and higher, the limit is 4 times in 2 minutes.
So you could scan 4 times with 1 second of delay in between. But you would not get any further scan results for the next 126 seconds.
So the fastest interval where every scan is successful would be every 30 seconds.
Related
I need to check my mobile signal strength every 10 secs to send data to server.
I have some data in sqlite. After these 10 seconds gone I send the data.
I have a service with a listener to get the changes on signal. The verification works fine when service start but I can't get signal data throughout the lifecycle. My application has some activities and this service starts in the very beginning.
How can I timely verify the signal in the service and and my data?
public MyService extends IntentService{
public MyService(){
...
telephonyManager = (TelephonyManager) App.getContext().getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(
new PhoneStateListener(){
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength){
int signalStrengthPercentage = 0;
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
if(signalStrength != null && gsmSignalStrength != 99){
signalStrengthPercentage = (int) ((float) gsmSignalStrength / 31 * 100);
}
signalIsStrength = signalStrengthPercentage >= 50;
Log.e("ERROR", "Percentage: "+signalStrengthPercentage);
}
}, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
}
Loop this and you will get the result.
long delay = 10000;
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
//your code to verify the signal strength
}
}, delay);
I am fairly new to android application development. I am working on an android application to ping access points to access their RSSI values to estimate a user's location.
While I currently have this 'working', I believe there to be a bug within my implementation that is creating too many calls to "onReceive()". Over the life of the application, the amount of calls to this function ramps up on a linear scale.
My goal with the code I'm about to post is to simply scan WiFi access points, get their RSSI values and then continuously loop. Battery life is no issue, performance is a much more important metric.
MainActivity.java:
Handler handler = new Handler();
final Runnable locationUpdate = new Runnable() {
#Override
public void run() {
getLocation();
//customView.setLocation(getX_pixel(curLocation.getX()), getY_pixel(curLocation.getY()));
//customView.invalidate();
handler.postDelayed(locationUpdate, 1000);
}
};
private void getLocation() {
Context context = getApplicationContext();
WifiScanReceiver wifiReceiver = new WifiScanReceiver();
registerReceiver(wifiReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiManager.startScan();
Log.d("START SCAN CALLED", "");
}
Then in the same file, in the onCreate() method:
handler.post(locationUpdate);
Then in the same file, outside of the onCreate() method:
class WifiScanReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context c, Intent intent) {
WifiManager wifiManager = (WifiManager) c.getSystemService(Context.WIFI_SERVICE);
List<ScanResult> scan = wifiManager.getScanResults();
// Application specific code:
sortScan(scan);
count+= 1;
System.out.println("Count: " + count);
}
}
};
I confirmed the ramping/thread problem because I incremented and output to the console when the program reaches "sortScan(scan)", and you can clearly see that the results are linearly ramping.
Like I said early, my intention is to simply re-scan as soon as the first scan finishes and loop it for the entire life of the application.
Any help will be greatly appreciated, thank you.
You are repeatedly registering your receiver which is not necessary.Just register the WifiScanReceiver only once in onCreate(). Then call start scan in the getLocation() function.
WifiManager wifiManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
Context context = getApplicationContext();
WifiScanReceiver wifiReceiver = new WifiScanReceiver();
registerReceiver(wifiReceiver, new
IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
wifiManager =
(WifiManager)context.getSystemService(Context.WIFI_SERVICE);
Handler handler = new Handler();
final Runnable locationUpdate = new Runnable() {
#Override
public void run() {
getLocation();
//This line will continuously call this Runnable with 1000 milliseconds gap
handler.postDelayed(locationUpdate, 1000);
}
};
private void getLocation() {
wifiManager.startScan();
Log.d("START SCAN CALLED", "");
}
}
class WifiScanReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context c, Intent intent) {
if(intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)){
//New scan results are available. Arrange a callback to the activity here.
}
}
}
You should not do heavy processing in the onReceive(). Arrange a callback to the Activity to do that.
You're creating a new broadcast receiver every time you run getLocation(). Every one of those receivers gets the WifiManager.SCAN_RESULTS_AVAILABLE_ACTION broadcast. Try allocating and registering the receiver once in an appropriate context, and only calling startScan() in getLocation().
The best way to continuously loop a scan for RSSI values for WiFi APs is to simply start the first scan in the OnCreate. Then in the onReceive callback from the BroadcastReceiver, call start scan again.
Here is my code:
public class FloatWifiManager implements IWifiManager {
private WifiManager wifiManager;
private BroadcastReceiver wifiScanReceiver;
public FloatWifiManager(Context context) {
...
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
// Registering Wifi Receiver
wifiScanReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context c, Intent intent) {
if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
// not getting called, only after running app and manually going to the wifi settings in android
}
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
context.registerReceiver(wifiScanReceiver, intentFilter);
wifiManager.startScan();
}
I registered the BroadcastReceiver exactly like I saw in all the examples, and did startScan.
What happens is, the wifi list is changing (for sure, I tested), but onReceive is not called if I just stay in the app.
What makes onReceive finally to be called - is to launch the app, leave it running, and going in the android phone to Settings -> Wifi settings. when going there, all of the sudden the List is updating and onReceive is called.
What's the problem here?
Does wifiManager.startScan(); runs the scan only once? or it is a function that keeps listening to incoming "Scan Results"?
And obviously, why does the receiver doesn't get called?
Yes, startScan() requests only one single scan.
You can get rid of the if (intent.getAction().equals(..)) condition. Anything else seems to be ok.
just to make it clear - my goal to have a receiver that will get
called every time the Wifi networks list are changing, without having
to click a "start scan" button.
AFAIK it is not possible to get notified whenever any of the wifi networks change. You can only request a scan with startScan - and of course you can call startScan repeatedly using a Thread or Handler.
The docs say that SCAN_RESULTS_AVAILABLE_ACTION is called when "an access point scan has completed, and results are available from the supplicant". How and when a scan is proceeded depends on the implemention of the supplicant. Elenkov writes, that "Android devices rarely include the original wpa_supplicant code; the included implementation is often modified for better compatibility with the underlying SoC".
Scan for access points
This example scans for available access points and ad hoc networks. btnScan activates a scan initiated by the WifiManager.startScan() method. After the scan, WifiManager calls the SCAN_RESULTS_AVAILABLE_ACTION intent and the WifiScanReceiver class processes the scan result. The results are displayed in a TextView.
public class MainActivity extends AppCompatActivity {
private final static String TAG = "MainActivity";
TextView txtWifiInfo;
WifiManager wifi;
WifiScanReceiver wifiReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wifi=(WifiManager)getSystemService(Context.WIFI_SERVICE);
wifiReceiver = new WifiScanReceiver();
txtWifiInfo = (TextView)findViewById(R.id.txtWifiInfo);
Button btnScan = (Button)findViewById(R.id.btnScan);
btnScan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i(TAG, "Start scan...");
wifi.startScan();
}
});
}
protected void onPause() {
unregisterReceiver(wifiReceiver);
super.onPause();
}
protected void onResume() {
registerReceiver(
wifiReceiver,
new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
);
super.onResume();
}
private class WifiScanReceiver extends BroadcastReceiver {
public void onReceive(Context c, Intent intent) {
List<ScanResult> wifiScanList = wifi.getScanResults();
txtWifiInfo.setText("");
for(int i = 0; i < wifiScanList.size(); i++){
String info = ((wifiScanList.get(i)).toString());
txtWifiInfo.append(info+"\n\n");
}
}
}
}
Permissions
The following permissions need to be defined in AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
android.permission.ACCESS_WIFI_STATE is necessary for calling WifiManager.getScanResults(). Without android.permission.CHANGE_WIFI_STATE you cannot initiate a scan with WifiManager.startScan().
When compiling the project for api level 23 or greater (Android 6.0 and up), either android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION must be inserted. Furthermore that permission needs to be requested, e.g. in the onCreate method of your main activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
String[] PERMS_INITIAL={
Manifest.permission.ACCESS_FINE_LOCATION,
};
ActivityCompat.requestPermissions(this, PERMS_INITIAL, 127);
}
I've spent days trying to get the android ADK connection running in a service rather then activity..... Anyone know if its even possible?
I would like to have the service handled inputStream and outputStream so I can read my Arduino for extended periods in the background.
When the activity returns to focus I will bind with the service and update the GUI. If this is possible I would eventually like to update a website with the live data from the service for remote monitoring.
Any help if appreciated. I'm new to programming and can't seem to find much info on this topic.
Thank you in advace for the help.
I'm also trying it and I've found out this.
http://robotgrrl.com/blog/2011/11/29/android-adk-background-service/
I was able to get a ADK connection running in the following way (not complete code. Only the basic building blocks):
First I have an activity that the receives adk intent broadcasts (android system service bases on the adk meta data and the manifest).
private static final String USB_ACCESSORY_ATTACHED = "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().getAction() != null && getIntent().getAction().equals(USB_ACCESSORY_ATTACHED)) {
Intent service = new Intent(this, ADKservice.class);
service.putExtras(getIntent());
startService(service);
Intent launch = new Intent(this, MainActivity.class);
launch.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(launch);
}
this.finish();
}
If the intent matches the adk string it will start the adk service and pass the intent information to the service, launch the user interface activity and finish itself.
The user interface (MainActivity) now binds to the service just like any other service so it can call public methods and/or receive data via the service callbacks (local broadcast's can also be used).
The ADKservice extends Runnable to monitor the usb connection. It also registers a receiver for adk disconnect so it can stop if the device gets disconnected:
#Override
public void onCreate() {
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(mUsbReceiver, filter);
mNotificationManager =(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("ADK Service")
.setContentText("Started");
startForeground(notifyID, mBuilder.build());
super.onCreate();
}
After onCreate has finished the service will call onStartCommand where the adk initialization starts.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartup " + mAccessory );
mAccessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (mAccessory != null) {
openAccessory(mAccessory);
}
return super.onStartCommand(intent, flags, startId);
}
private void openAccessory(UsbAccessory accessory) {
Log.d(TAG, "openAccessory: " + accessory);
UsbManager mUsbManager = (UsbManager) getApplicationContext().getSystemService(Context.USB_SERVICE);
mFileDescriptor = mUsbManager.openAccessory(accessory);
if (mFileDescriptor != null) {
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
thread = new Thread(null, this, "ADKserviceThread");
thread.start(); // start runnable
}
public void run() {
// handle adk "usb" messages here
}
#Override
public void onDestroy() {
closeAccessory();
stopForeground(true);
super.onDestroy();
}
private void closeAccessory() {
try {
if (mFileDescriptor != null) {
mFileDescriptor.close();
}
} catch (IOException e) {
} finally {
mFileDescriptor = null;
mAccessory = null;
}
}
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
closeAccessory();
stopSelf();
}
}
};
The handling of the connection might need some tweaks but the overall concept seems to work. I hope that helps everyone! It seems easy now but it took me a long time to here (I'm repetitively new to programming)
You should handle the loading of the accessory in the main activity like here, and then pass a reference to the accessory object to a service.
You can now bind the service from any activity and get access to the input/output streams.
You can also close the activity, and when you disconnect the accessory, the activity should catch the intent broadcast and relaunch to perform the closeAccessory routine.
I want to show the connection process on the screen when my device is connecting to the wifi network. SUPPLICANT_STATE_CHANGED_ACTION is provided by WifiManager but i don't know how to use it. Can anyone help me please?
You can indeed use the broadcasted intents for SUPPLICANT_STATE_CHANGED_ACTION:
The app needs the permission in its Manifest file:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
Then register for the system broadcast:
MyWifiStateReceiver handler = new MyWifiStateReceiver();
context.registerReceiver(handler, new IntentFilter(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
the registerReceiver() needs an instance of a class implementing BroadcastReceiver as its first argument. In that code you can act on the Wifi state changes by overriding the onReceive method. For example
public class MyWifiStateReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION))
{
SupplicantState state = (SupplicantState) intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
switch(state)
{
case COMPLETED:
case DISCONNECTED:
...
}
}
}
}
For the possible Wifi state values, see http://developer.android.com/reference/android/net/wifi/SupplicantState.html
I don't know of a callback method that lets you know when the wifi status has changed. I polled the information using a Handler running in the background.
Add the handler to your class.
private WifiStatusHandler wifiStatusHandler = new WifiStatusHandler();
Start it by calling
wifiStatusHandler.start();
The code I used is below.
/**
* Checks for wifi status updates.
*/
private class WifiStatusHandler extends Handler {
private boolean running = false;
public void handleMessage(Message message) {
if (running) {
//check wifi status here
WifiManager wifiMgr = (WifiManager) getSystemService(Context.WIFI_SERVICE);
int curWifiState = wifiMgr.getWifiState();
SupplicantState info = wifiMgr.getConnectionInfo().getSupplicantState();
WifiInfo curWifi = wifiMgr.getConnectionInfo();
Log.i(TAG,"WIFI STATE = " + info.toString());
//update the TextView etc.
sleep();
}
}
private void sleep() {
removeMessages(0);
sendMessageDelayed(obtainMessage(0), REFRESH_DELAY);
}
public synchronized void start() {
running = true;
removeMessages(0);
sendMessageDelayed(obtainMessage(0), 0);
}
public synchronized void stop() {
running = false;
}
}