I am trying to test GeoFences for a location aware application. I have gotten as many as 11 consecutive successes, but that is typically followed by dozens of failures. Rebooting the device does not help. Uninstalling/reinstalling does not help. Disabling location, re-enabling location does not help. Changing the devices Location Accuracy settings does not help.
I've tested on v2.3.4, v4.0.3, v4.4.2, v4.4.4 and 2 v5.0.1 physical devices. One of the Lollipop devices is a device with cellular service. The remaining devices are SIMless and used for testing only. Side note: the devices without service struggle with setting their location via MockLocation, but if they manage to set their location they almost never register a fence entry/exit.
Location is being updated via Mock Location well enough, but the GeoFence(s) are not being triggered with any reliability.
I've Googled it. I've looked at the documentation on developer.android.com site. I've searched StackOverflow. In fact, many of the suggestions made by others have been implemented in the code below.
So, how can I reliably trigger my GeoFence using MockLocation?
import android.location.Location;
import android.location.LocationManager;
public class GeoFenceTester extends ActivityInstrumentationTestCase2<GeoFenceTesterActivity> {
public static final String TAG = GeoFenceTester.class.getSimpleName();
private LocationManager locationManager;
private Activity activityUnderTest;
protected void setUp() throws Exception {
super.setUp();
activityUnderTest = getActivity();
/*
MOCK Locations are required for these tests. If the user has not enabled MOCK Locations
on the device or emulator these tests cannot work.
*/
if (Settings.Secure.getInt(activityUnderTest.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 0) {
throw new RuntimeException("Mock locations are currently disabled in Settings - These tests require mock locations");
}
Log.i(TAG, "Setup MOCK Location Providers");
locationManager = (LocationManager) activityUnderTest.getSystemService(Context.LOCATION_SERVICE);
Log.i(TAG, "GPS Provider");
locationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, true, false, false, false, false, false, Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);
Log.i(TAG, "Network Provider");
locationManager.addTestProvider(LocationManager.NETWORK_PROVIDER, true, false, true, false, false, false, false, Criteria.POWER_MEDIUM, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LocationManager.NETWORK_PROVIDER, true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.wtf(TAG, String.format("Location Accuracy: %1$d", Settings.Secure.getInt(activityUnderTest.getContentResolver(), Settings.Secure.LOCATION_MODE)));
}
/* Our EventBus for onEvent() callbacks */
EventBus.getDefault().register(this);
}
protected void tearDown() {
/* Our EventBus for onEvent() callbacks */
EventBus.getDefault().unregister(this);
locationManager.removeTestProvider(LocationManager.GPS_PROVIDER);
locationManager.removeTestProvider(LocationManager.NETWORK_PROVIDER);
locationManager = null;
activityUnderTest = null;
super.tearDown();
}
public void testGeoFence() {
/*
Using one or the other or both of these makes no difference.
*/
Location mockGpsLocation = new Location(LocationManager.GPS_PROVIDER);
mockGpsLocation.setLatitude(32.652411);
mockGpsLocation.setLongitude(-79.938063);
mockGpsLocation.setAccuracy(1.0f);
mockGpsLocation.setTime(System.currentTimeMillis());
Location mockNetworkLocation = new Location(LocationManager.NETWORK_PROVIDER);
mockNetworkLocation.setLatitude(32.652411);
mockNetworkLocation.setLongitude(-79.938063);
mockNetworkLocation.setAccuracy(1.0f);
mockNetworkLocation.setTime(System.currentTimeMillis());
/*
setElapsedRealtimeNanos() was added in API 17
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mockGpsLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
mockNetworkLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
try {
Method locationJellyBeanFixMethod = Location.class.getMethod("makeComplete");
if (locationJellyBeanFixMethod != null) {
locationJellyBeanFixMethod.invoke(mockGpsLocation);
locationJellyBeanFixMethod.invoke(mockNetworkLocation);
}
} catch (Exception e) {
// There's no action to take here. This is a fix for Jelly Bean and no reason to report a failure.
}
for (int i = 0; i < 30; i++){
Log.i(TAG, String.format("Iterating over our location ... (%1$d)", i));
locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, mockGpsLocation);
locationManager.setTestProviderLocation(LocationManager.NETWORK_PROVIDER, mockNetworkLocation);
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
}
public void onEvent(final GeoFenceUpdateEvent event) {
// This doesn't get called about 9/10 times.
// I have a GeoFence with a 5000m radius around 32.652411, -79.938063
}
public void onEvent(final LocationUpdateEvent event) {
// This gets called without incident and event.getLatitude() & event.getLongitude() are correct.
}
}
Even with your location tracking turned off, a geofence can still establish your general location through your device's unique IP address.
Most smart devices allow you to set them up to function so that they trigger automatically via a wonky-sounding technology called geofencing. That simply means enabling your smart devices to automatically activate when you leave home or return—an event that is (usually) determined by the location of your smartphone.
How geofencing works. To make use of geofencing, an administrator or developer must first establish a virtual boundary around a specified location in GPS- or RFID-enabled software. This can be as simple as a circle drawn 100 feet around a location on Google Maps, as specified using APIs when developing a mobile app.
The Geofence manager will consider all system-wide location requests equally, not just those made by your specific application. If you have any other applications or services on the device that are currently using a Location Services, these external applications be producing Lat/Lon locations used in consideration when triggering your Geofences.
Given you are producing Mock Locations, it is possible that external applications or services are producing conflicting LocationUpdates, and that these jumps between Mock and Real locations are causing your unstable Geofence test results.
You may be able to confirm this by disabling your real Location Services in settings and purely using Mock Locations, or look for any applications that may be currently using Location Services and re-testing once those specific apps have been stopped or disabled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With