Currently, I'm developing an application that injects locations into Android. This application should inject locations at specific moments. Before and after, normal GPS should be used.
My software is able to switch to mock locations and inject locations succesfully. But I am not able to make Android listen to normal GPS again.
My code to set-up:
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, false, false, false, true, true, true, 0, /*magic*/5);
mLocationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);
My failing attempt to stop:
mLocationManager.setTestProviderEnabled(PROVIDER_NAME, false);
mLocationManager.removeTestProvider(PROVIDER_NAME);
My question: how to stop provide mock locations and resume default Android GPS?
I found out it was due to two reasons.
First, the setTestProviderEnabled(PROVIDER_NAME, false) call was not needed. Second, when looking into the Android source code, it showed that the normal GPS will not be activated automatically. By requesting location updates after the removeTestProvider() call, the normal GPS signal will be activated and everything works fine.
Related
My Android app uses the position provided by the gps.
One of my classes overrides the onProviderDisabled method of LocationListener.
The app is working fine and gets the gps position.
I have an Espresso test that create a test provider for mock locations in the #Before setup() method of my test class:
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
locationManager.addTestProvider(LOCATION_PROVIDER_NAME, false,
false, false, false,
true, true, true,
POWER_USAGE_HIGH,
ProviderProperties.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LOCATION_PROVIDER_NAME, false);
Then in my test, I call
locationManager.setTestProviderEnabled(LOCATION_PROVIDER_NAME, true);
SystemClock.sleep(200);
...
test if gps is enabled
...
On an Android 8 device, this code works fine (onProviderDisabled not called)
But on Android 11, 12 and 13 devices, about 300 ms after enabling the test provider, the onProviderDisabled is called.
What do I need to do to enable mock location on new Android devices?
I finally found the solution. It is so easy when you know it !
On recent Android devices, when using mock locations in Espresso test, you must switch ON the location service manually before running the test.
Note: this was not required on Android 8 devices
I wrote an app which can obtain GNSS raw measurement data using
LocationManager.registerGnssMeasurementsCallback(...)
The app can process this data and calculate a new positions. Now, I would like to mock this calculated positions into the Android OS, so other apps can work with this position.To realize this, I can give the app location mocking permissions, add it to the list of location mocking apps in the developer options and work with methods like
LocationManager.addTestProvider(...)
LocationManager.setTestProviderEnabled(...)
LocationManager.setTestProvderLocation(...)
First, I need to decide what location provider I want to mock. PASSIVE_PROVIDER is not allowed to be mocked. NETWORK_PROVIDER can be mocked, but the user would have to select this locating method in the system settings. If he does this, the system stops sending GNSS measurements callbacks to any app which has registered these callbacks, so no position can be calculated anymore. A callback with a status change "GNSS measurements listener disabled" is invoked in this case. The same thing happens when I try to mock GPS_PROVIDER (as soon as setTestProviderEnabled() is called). So I thought about mocking the fused location provider. Since my Android Studio linker cannot resolve LocationManager.FUSED_PROVIDER (no idea why) I directly used the String "fused" and tried to mock the locations. No exceptions come up and gnss raw measurements can still be received. However, on GoogleMaps, I only see the native locations provided by the internal chip. This is the case no matter if the location method "GPS only" or "best" is selected in the system.
So, how can I solve this? Is there any way to mock GPS_PROVIDER without interrupting raw gnss measurements or preventing gnss chip locations to be applied to the fused provider while I am mocking it?
Btw, my code looks like this:
locationManager.addTestProvider("fused" , false, false, false, false, false, false, false, Criteria.POWER_LOW, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled("fused", true);
Location location = new Location("fused");
location.setLatitude(lat);
location.setLongitude(lon);
location.setAltitude(alt);
location.setAccuracy(1.0f);
location.setTime(System.currentTimeMillis());
location.setElapsedRealtimeNanos(System.nanoTime());
locationManager.setTestProviderLocation("fused", location);
I have these two lines of code ready to be used in the section of my app where the user's location is requested:
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 0, locationListener);
The problem is that, when testing in real device, I have noticed that, using GPS_provider does not work very well when being in certain places like inside of a building....the app is stuck waiting to acquire GPS coordinates which take a lot of time or just never come unless I have open-sky conditions.
The question would be: how do I do to still use GPS_providerby default but, if gps coordinates take more than XXXX seconds to be acquired, then switch to Network_providerand acquire position from Network.
Small edit: is it maybe any way to check GPS signal strength before using GPS or Network provider?
LocationListener has a function onStatusChanged(). You have to override this function to check GPS_PROVIDER's status and accordingly take necessary action , which in your case is switching to NETWORK_PROVIDER.
Hope this answers your query
I have some code that runs multiple times per second in my app. I'm trying to get my location in every cycle. I am using the following:
Location myLastPos = LocationServices.FusedLocationApi.getLastLocation(googleApiClient)
My app also runs in the background using a PARTIAL_WAKE_LOCK. With the screen on everything seems OK. When I turn the screen off my app still runs normally but I no longer get location updates consistently.
It appears that I get updates much less frequently (often minutes in between updates). I'm checking the timestamp of the location using:
myLastPos.getElapsedRealtimeNanos()
I also found that even when the screen is on I get some strange results. Sometimes I get a few milliseconds between updates, other times I get a few seconds. This is all very concerning. Can someone either help me use FusedLocationApi properly or suggest an alternative. All I really want is to poll the gps directly for lat/long a few times a second without google libraries getting in the way.
The getLastLocation() method just gets the last known location that the device happens to know. The "last known location" here means exactly that: It may not be up-to-date. Locations do come with a time stamp which could be used to asses if the location might still be relevant.
The device doesn't determine its location on its own, but only when some application request the location. So your app is now dependent on other applications requesting location updates.
If you need updates every few seconds, then request regular location updates yourself.
Android documentation recommends the FusedLocationProvider, but the LocationManager is also a perfectly valid option, if there's any reason to avoid the Google Play services.
The basic idea is to first request location updates:
// Using LocationManager as an example.
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// Using GPS, requesting location updates as soon as available and even for
// the smallest changes. Here 'this' refers to our LocationListener
// implementation.
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
The updates are then received by a listener:
#Override
public void onLocationChanged(Location location) {
// We received a location update.
// Copy the value from the method parameter to our
// class member variable.
mLocation = location;
}
And when you no longer need the updates you should cancel the request:
mLocationManager.removeUpdates(this);
The approach is very similar for the FusedLocationProvider.
I'm creating app to listen to location changes, in onResume() I call these two methods:
_locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 10,locationListenerGps);
_locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 10, locationListenerNetwork);
When I do that, I never get location from GPS_PROVIDER, only from NETWORK_PROVIDER.
What more, when I get location from NETWORK and it gets to method onStatusChanged() (resulting in TEMPORARILY_UNAVAILABLE) app is stucked and nothings going on. There are no location updates at all, until I restart app.
I'll be thankful for any help in discovering why it's not working like I thought it might...