ACCESS_COARSE_LOCATION permission gives a cell tower precision on Android

poiuytrez picture poiuytrez · May 12, 2015 · Viewed 38.5k times · Source

I am making some tests with the requestLocationUpdates() function from the FusedLocationApi. I am using the PRIORITY_BALANCED_POWER_ACCURACY. A city block precision is fine for me.

When I request the ACCESS_FINE_LOCATION permission, I get around a 100m precision which is great with GPS off. As I do not need a GPS precision but a city block precision, I would like to request only the ACCESS_COARSE_LOCATION permission. However when I request the ACCESS_COARSE_LOCATION permission, I get a 2 km precision. It seems that the device does not use anymore the Wifi permission and only a cell tower precision.

How can I have a better precision with the ACCESS_COARSE_LOCATION permission?

Note: the GPS is disabled on my test device.

Answer

Daniel Nugent picture Daniel Nugent · May 19, 2015

This is an interesting problem, and I was under the impression that using ACCESS_COARSE_LOCATION would use WiFi, since that's what the documentation says.

The documentation for ACCESS_COARSE_LOCATION states:

Allows an app to access approximate location derived from network location sources such as cell towers and Wi-Fi.

So, I put it to the test, and the results are surprising.

Here is the code that I used to test with:

public class MainActivity extends Activity implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    LocationRequest mLocationRequest;
    GoogleApiClient mGoogleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        buildGoogleApiClient();
        mGoogleApiClient.connect();
    }

    @Override
    protected void onPause(){
        super.onPause();
        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }
    }

    protected synchronized void buildGoogleApiClient() {
        Toast.makeText(this,"buildGoogleApiClient",Toast.LENGTH_SHORT).show();
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    public void onConnected(Bundle bundle) {
        Toast.makeText(this,"onConnected",Toast.LENGTH_SHORT).show();

        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(10);
        mLocationRequest.setFastestInterval(10);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        //mLocationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER);
        //mLocationRequest.setSmallestDisplacement(0.1F);

        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }

    @Override
    public void onConnectionSuspended(int i) {
        Toast.makeText(this,"onConnectionSuspended",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Toast.makeText(this,"onConnectionFailed",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onLocationChanged(Location location) {

        Log.d("locationtesting", "accuracy: " + location.getAccuracy() + " lat: " + location.getLatitude() + " lon: " + location.getLongitude());

        Toast.makeText(this,"Location Changed",Toast.LENGTH_SHORT).show();
    }
}

AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

build.gradle:

compile 'com.google.android.gms:play-services:7.3.0'

The first test I did was with PRIORITY_BALANCED_POWER_ACCURACY, and no WiFi. Note that I also disabled Always Allow Scanning, since it states:

Let Google Location Service and other applications scan for Wi-Fi networks, even when Wi-Fi is off

So, that would certainly skew the results if it was enabled.

Note that I also had Location Mode set in Battery Saving Mode for all tests, so the GPS radio was off the entire time.

Here are the results of PRIORITY_BALANCED_POWER_ACCURACY, ACCESS_COARSE_LOCATION, and no WiFi:

accuracy: 2000.0 lat: 37.78378378378378 lon: -122.40320943772021

So, it says 2000 meter accuracy, and here is how far away the actual coordinates are, the green arrow shows where I actually am:

enter image description here

Then, I enabled WiFi, and ran the test again, and surprisingly, the results were exactly the same!

accuracy: 2000.0 lat: 37.78378378378378 lon: -122.40320943772021

Then, I switched to LocationRequest.PRIORITY_LOW_POWER in the LocationRequest while keeping android.permission.ACCESS_COARSE_LOCATION in the AndroidManifest.xml.

No WiFi:

accuracy: 2000.0 lat: 37.78378378378378 lon: -122.40320943772021

With WiFi:

accuracy: 2000.0 lat: 37.78378378378378 lon: -122.40320943772021

The results were exactly the same again! Using PRIORITY_LOW_POWER had the same results as using PRIORITY_BALANCED_POWER_ACCURACY, in that the WiFi state did not seem to have any effect on accuracy of coordinates.

Then, just to cover all the bases, I changed back to LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY, and switched the AndroidManifest.xml to ACCESS_FINE_LOCATION :

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

First test, no WiFi:

accuracy: 826.0 lat: 37.7825458 lon: -122.3948752

So, it says accuracy of 826 meters, and here is how close it was on the map:

enter image description here

Then, I powered on WiFi, and here is the result:

accuracy: 18.847 lat: 37.779679 lon: -122.3930918

It's literally spot on, as you can see on the map:

enter image description here

It seems that it matters less what you use in your LocationRequest in the Java code, and more what permission you use in the AndroidManifest.xml, since the results here clearly show that when using ACCESS_FINE_LOCATION, having the WiFi radio on or off made a huge difference in the accuracy, and it was also more accurate in general.

It certainly seems as though the documentation is a bit miss-leading, and that while using android.permission.ACCESS_COARSE_LOCATION, having the WiFi radio on or off doesn't make a difference when your app is the only one making location requests.

Another thing that the documentation states is that using PRIORITY_BALANCED_POWER_ACCURACY will let your app "piggy-back" onto location requests made by other apps. From the documentation:

They will only be assigned power blame for the interval set by setInterval(long), but can still receive locations triggered by other applications at a rate up to setFastestInterval(long).

So if the user opens up Google Maps, according to the documentation your app can obtain a more accurate location at that point. That is one of the major up-sides of using the new Fused Location Provider rather than the older APIs, since it decreases the amount of battery drain of your app without much work on your part.

Edit: I performed a test of this functionality, to see what would happen while using ACCESS_COARSE_LOCATION.

First Test: ACCESS_COARSE_LOCATION, PRIORITY_BALANCED_POWER_ACCURACY, and WiFi on:

accuracy: 2000.0 lat: 37.78378378378378 lon: -122.38041129850662

That placed me out in the water, quite far from my current location. Then, I exited the test app, launched Google Maps, which located me exactly where I am, then re-launched the test app. The test app was not able to piggy-back onto the location from Google Maps, and the result was exactly the same as before!

accuracy: 2000.0 lat: 37.78378378378378 lon: -122.38041129850662

I re-tested this a few times, just to be sure, but it really looks like using ACCESS_COARSE_LOCATION also disables the ability of apps to "piggy-back" on to locations obtained by other apps.

It looks like using ACCESS_COARSE_LOCATION in the AndroidManifest.xml really cripples the app in terms of getting precise location data.

In conclusion, the only thing you can really do is hone in on the best combination of settings that work for you and your app, and hopefully the results of this test can help you make that decision.