gyamana
gyamana

Reputation: 1231

ACCESS_FINE_LOCATION SecurityException despite specifying the permission in the manifest file

My app is attempting to access the device's location and I have included the following in the AndroidManifest.xml:

<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.app">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application>
        <meta-data android:name="com.google.android.gms.version" />
    </application>

</manifest>

I have implemented the GoogleApiClient.ConnectionCallbacks as follows to access the location service:

public class BackgroundLocationService implements
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    private static final String TAG = BackgroundLocationService.class.getSimpleName();

    private static GoogleApiClient googleApiClient;
    private static PendingIntent locationCallback;

    public static int LOCATION_INTERVAL = 10000;
    public static int FAST_INTERVAL = 5000;

    @Override
    public void onConnected(Bundle bundle) {
        Log.i(TAG, "Connected to Google API");

        LocationRequest request = new LocationRequest();
        request.setInterval(LOCATION_INTERVAL);
        request.setFastestInterval(FAST_INTERVAL);
        request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, locationCallback);
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.i(TAG, Integer.toString(i));
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i(TAG, connectionResult.toString());
    }
}

When I trigger the location service call, the following exception is thrown:

java.lang.SecurityException: Client must have ACCESS_FINE_LOCATION permission to request PRIORITY_HIGH_ACCURACY locations.
    at android.os.Parcel.readException(Parcel.java:1599)
    at android.os.Parcel.readException(Parcel.java:1552)
    at com.google.android.gms.location.internal.zzi$zza$zza.zza(Unknown Source)
    at com.google.android.gms.location.internal.zzk.zza(Unknown Source)
    at com.google.android.gms.location.internal.zzl.zza(Unknown Source)
    at com.google.android.gms.location.internal.zzd$7.zza(Unknown Source)
    at com.google.android.gms.location.internal.zzd$7.zza(Unknown Source)
    at com.google.android.gms.internal.zzlb$zza.zzb(Unknown Source)
    at com.google.android.gms.internal.zzlf.zza(Unknown Source)
    at com.google.android.gms.internal.zzlf.zzb(Unknown Source)
    at com.google.android.gms.internal.zzli.zzb(Unknown Source)
    at com.google.android.gms.location.internal.zzd.requestLocationUpdates(Unknown Source)
    at com.localz.spotzpush.sdk.service.BackgroundLocationService.onConnected(BackgroundLocationService.java:45)
    at com.google.android.gms.common.internal.zzk.zzh(Unknown Source)
    at com.google.android.gms.internal.zzlg.zznU(Unknown Source)
    at com.google.android.gms.internal.zzlg.onConnected(Unknown Source)
    at com.google.android.gms.internal.zzli$2.onConnected(Unknown Source)
    at com.google.android.gms.common.internal.zzj$zzg.zzpf(Unknown Source)
    at com.google.android.gms.common.internal.zzj$zza.zzc(Unknown Source)
    at com.google.android.gms.common.internal.zzj$zza.zzt(Unknown Source)
    at com.google.android.gms.common.internal.zzj$zzc.zzph(Unknown Source)
    at com.google.android.gms.common.internal.zzj$zzb.handleMessage(Unknown Source)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

I am using an actual device to produce this issue and have been scratching my head on why the exception is thrown.

Upvotes: 9

Views: 13660

Answers (4)

gyamana
gyamana

Reputation: 1231

The issue was caused by a new feature for Android 6.0.

Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app.

http://developer.android.com/training/permissions/requesting.html

To get around the issue when the permission is not explicitly granted, I had put in a check to wrap location service calls (as per the Google Documentation, slightly modified).

int permissionCheck = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION);

if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
    //Execute location service call if user has explicitly granted ACCESS_FINE_LOCATION..
}

The Google Documentation also steps through how to request for the permissions that are needed.

UPDATED 12th Oct 2016

Adding in @Siddharth's update to include a more helpful answer for checkSelfPermission()

//For this example, run the check onResume()
@Override
public void onResume() {

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {

            // Show an expanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.

        } else {

            // No explanation needed, we can request the permission.
            // PERMISSION_REQUEST_ACCESS_FINE_LOCATION can be any unique int
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_ACCESS_FINE_LOCATION);
        }
    }
}

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_ACCESS_FINE_LOCATION: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

Upvotes: 16

Siddharth
Siddharth

Reputation: 9574

Extract from Google Documentation. Seems like the most accepted answer on this thread is missing some critical information about the call back of the async checkSelfPermission.

Please confirm. If not, I'll delete this answer.

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

Upvotes: 2

F34R
F34R

Reputation: 33

For everybody else having an error with gyamana's answer at Manifest.permission.ACCESS_FINE_LOCATION error. Make sure you are using android.Manifest instead of my.app.package.Manifest.

Upvotes: 0

Anoop M Maddasseri
Anoop M Maddasseri

Reputation: 10529

Default implmentation of LocationSource doesn't necessarily have to use GPS to return accurate locations.

If i remove the permission "ACCESS_FINE_LOCATION" in the Manifest file, as soon as I try to display the map, the app crashes

This is because the default implementation uses LocationClient with LocationRequest.PRIORITY_HIGH_ACCURACY.

GoogleMap.setMyLocationEnabled when you request only ACCESS_COARSE_LOCATION permission, but switches to LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY.

See this Android: Google Maps location with low battery usage

Upvotes: 0

Related Questions