web-dev-qa-db-fra.com

ACCESS_FINE_LOCATION SecurityException malgré la spécification de l'autorisation dans le fichier manifeste

Mon application tente d'accéder à l'emplacement de l'appareil et j'ai inclus les éléments suivants dans le 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>

J'ai implémenté le GoogleApiClient.ConnectionCallbacks comme suit pour accéder au service de localisation:

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());
    }
}

Lorsque je déclenche l'appel du service de localisation, l'exception suivante est levée:

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)

J'utilise un appareil réel pour produire ce problème et je me suis demandé pourquoi une exception était lancée.

9
gyamana

Le problème était dû à une nouvelle fonctionnalité pour Android 6.0. 

A partir d'Android 6.0 (API niveau 23), les utilisateurs accordent des autorisations aux applications pendant l'exécution de l'application, et non lors de l'installation.

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

Pour contourner le problème lorsque l'autorisation n'est pas explicitement accordée, j'avais coché pour envelopper les appels de service de localisation (conformément à la documentation Google légèrement modifiée).

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..
}

La documentation Google explique également comment demander les autorisations nécessaires.

ACTUALISÉ le 12 octobre 2016

Ajout de la mise à jour de @ Siddharth pour inclure une réponse plus utile pour 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
    }
}
16
gyamana

Extrait de la documentation Google. On dirait que la réponse la plus acceptée sur ce fil manque des informations essentielles sur le rappel de la variable async checkSelfPermission.

Veuillez confirmer. Sinon, je vais supprimer cette réponse.

// 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
    }
}
2
Siddharth

Pour tout le monde ayant une erreur avec la réponse de gyamana à l'erreur Manifest.permission.ACCESS_FINE_LOCATION. Assurez-vous que vous utilisez Android.Manifest au lieu de my.app.package.Manifest.

0
F34R

L'implémentation par défaut de LocationSource ne doit pas nécessairement utiliser le GPS pour renvoyer des emplacements précis.

Si je supprime l'autorisation "ACCESS_FINE_LOCATION" dans le fichier Manifest, dès que j'essaie d'afficher la carte, l'application se bloque.

En effet, l'implémentation par défaut utilise LocationClient avec LocationRequest.PRIORITY_HIGH_ACCURACY.

GoogleMap.setMyLocationEnabled lorsque vous ne demandez qu'une autorisation ACCESS_COARSE_LOCATION, mais passez à LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY.

Voir ceci Android: emplacement de Google Maps avec une utilisation de la batterie faible

0
Anoop M