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
Related
My app collects locations with a foreground service, and it has been working well up through Android 11. With the Android 12 beta, with all permissions that work well for previous versions granted, I'm getting very few locations, well below my update interval settings, sometimes not for several minutes at a time.
Are there some Android 12 changes for running apps with a foreground service, with the LocationManager, or anything else that might affect this?
Edit: Tried mock locations; those come in fine. Also tried adding
android:foregroundServiceType="location"
... which didn't help. I've thought about switching to the Fused Location Provider API, but I've read that doesn't help to get more locations, just the opposite.
My next step is to use the "gps" location provider with the LocationManager; my guess is that will get more locations, but my app has been handling the types of locations from the "fused" provider, so I'd really like a better option.
Using FusedLocationProviderClient instead of LocationManager for Android 12 worked; it looks like using the "fused" provider with LocationManger.requestLocationUpdates with Android 12 will no longer cause new locations to be collected.
I am using the beacon library to scan for beacons with a foreground service and a persistent notification. I have tested this on both Android 9.0 and 7.0, and the app works as expected, and sends the beacons scanned to a server every 30 seconds. Now, I am trying to add location scanning to the app, so that it retrieves location updates every 30 seconds. I am using the Google Play API, and set up a location request with an interval of 30 secs. Then, I created a FusedLocationProvider client in my application class, so I gave it my app's (not activity's) context. Then, I gave my request and the following callback to the client:
locationCallback = new LocationCallback()
{
#Override
public void onLocationResult(LocationResult locationResult)
{
if ( locationResult != null )
{
Log.d(TAG, "location acquired: " + locationResult.getLastLocation());
beaconContainer.setLocation(locationResult.getLastLocation());
}
}
};
The beaconContainer object holds a list of beacons and the latest location (and a timestap of when the latest location was acquired using LocalTime.now()), and sends these to the server every 30 seconds. At first, the app seems to work and the location timestamp is within 30 seconds of when the request to the server was sent. However, after some time has passed (and the screen has been off for some time), it seems that the onLocationResult method in the callback is not being called and the location is not being updated. For example, the server request was made at 12:34 but the location was updated at 10:21. Note that the beacon scanning is still being correctly performed as expected.
I was wondering if this is because the phone that I tested this on was stationary, or if it is because I did not use a service for location updating. To me, it seems to be the former because my app has a foreground service (ble scanner) and a persistent notification, so according to the docs, it is in the foreground and it should not be subject to background limitations. If it is the latter, how can I fuse the beacon library's foreground service with my location scanning so that they both run as expected.
Thanks.
EDIT:
Here is a screenshot of battery historian, showing how BLE is regularly and consistently being used while GPS is used for intermittent periods.
The documentation for FusedLocationProviderClient indicates that on Android 8+, if the app is not in the foreground, you will only get updates a "few times each hour". See here. This is likely because the implementation inside Google Play Services uses the JobScheduler on Android 8+ to get around background service limits, and jobs are limited to running every 15 minutes +/- 5 minutes in the background. Since Google Play Services APIs are closed source and proprietary, it is difficult to say more about its internal implementation, but it is unlikely that it takes into account that your app has a foreground service. (The Android Beacon Library, by contrast, is explicitly designed to behave differently when configured with a Foreground Service so you get more frequent updates.)
It's unclear how the FusedLocationProviderClient works differently in the background on Android 7. It may not work differently at all, and may follow the pattern described above in the background simply if your app targets SDK 26 or higher. You'd have to test to make sure -- effectively reverse engineering Google Play Services. Even if you do figure it out, the behavior might change in the next Google Play Services version, and you'll never know about it unless you reverse-engineer it again. This is the peril of using closed-source SDKs.
An alternative would be to instead use the open-source location APIs provided by Android.
LocationManager locationManager = (LocationManager)
this.getSystemService(Context.LOCATION_SERVICE);
try {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 400l, (float) 1000.0, this); //You can also use LocationManager.GPS_PROVIDER and LocationManager.PASSIVE_PROVIDER
}
catch (SecurityException e) {
Log.d(TAG, "Can't get location -- permission denied");
}
Of course, you'll want to adjust the accuracy and update interval to suit your needs and conserve battery. And you will certainly find dropouts in callbacks when your phone enters Doze mode. But you should be able to use the above in your Application class as you describe in your question without the annoyingly opaque behaviors added by Google Play Services.
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 picked up Google mock location provider from here: http://developer.android.com/training/location/location-testing.html. I verified that it is indeed providing mock locations by opening Google Maps. The location tracker in maps keeps moving somewhere around Google's office, indicating it is responding to the mock locations. BUT, when I open my own app I do not get the mock locations. I continue to get the real locations. Do I have to make any changes to my app to get mock locations? One more bizarre thing is that even after I shut down the mock location provider , Google Maps continues to receive the mock locations. Only when I restart the phone it goes back to receiving real locations. Any ideas how to proceed? Any other options to test mock locations?
Tested on MotoG (4.4.2)
Edit:
Test code : https://github.com/nutiteq/hellomap3d/blob/master/HelloMap3D/src/com/nutiteq/hellomap/HelloMap3DActivity.java
This is just the nutiteq sample app. App works fine, but continues to receive real locations even when mock provider is running.
There is the standard requestLocationUpdates call and then an implementation of onLocationChanged callback.
OK, a bit too hasty placing a bounty. Sorted this by using the same Location Provider "gps" both in the LocationProvider app and in my app.
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.