Error de Android "gps requiere ACCESS_FINE_LOCATION", aunque mi archivo de manifiesto contiene esto

104

Cada vez que ejecuto la aplicación, se lanza mi SecurityException y el error del depurador se lee así:

java.lang.SecurityException: el proveedor de ubicación "gps" requiere el permiso ACCESS_COARSE_LOCATION o ACCESS_FINE_LOCATION.

Esto parece un simple error, sin embargo, mi archivo de manifiesto es completamente correcto. Aquí está, y aquí está mi código MapActivity también:

<?xml version="1.0" encoding="utf-8"?>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="com.dev.cromer.jason.coverme.permission.MAPS_RECEIVE" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
    <meta-data
        android:name="com.google.android.maps.v2.API_KEY"
        android:value= "@string/google_maps_key" />

    <activity
        android:name=".MapActivity"
        android:label="@string/title_activity_map" >
    </activity>
</application>

Mi actividad:

    package com.dev.cromer.jason.coverme;

import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapActivity extends FragmentActivity implements LocationListener {

    private GoogleMap mMap; // Might be null if Google Play services APK is not available.

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

        setUpMapIfNeeded();
    }

    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
    }



    private void setUpMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            // Try to obtain the map from the SupportMapFragment.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                    .getMap();

            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                //mMap.setMyLocationEnabled(true);
                //mMap.setOnMyLocationChangeListener(this);
                setUpMap();
            }
        }
    }


    private void setUpMap() {
        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
        mMap.setMyLocationEnabled(true);

        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        try {
            Location myLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

            if (myLocation != null) {
                Log.d("TAG", "Not null");
            }
            else {
                Log.d("TAG", "NULL");
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
            }
        }
        catch (SecurityException se) {
            Log.d("TAG", "SE CAUGHT");
            se.printStackTrace();
        }
    }


    @Override
    public void onLocationChanged(Location location) {
        Log.d("CHANGED", "LOCATION UPDATED");

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }
}
Jason Cromer
fuente
¿En qué versión de Android estás probando esto?
CommonsWare
4
No relacionado, pero si solicita una ubicación precisa, no es necesario que solicite información general. Esta incluido.
joey_g216

Respuestas:

136

ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATIONy WRITE_EXTERNAL_STORAGEson parte del sistema de permisos en tiempo de ejecución de Android 6.0 . Además de tenerlos en el manifiesto como lo hace, también debe solicitarlos al usuario en tiempo de ejecución (usando requestPermissions()) y ver si los tiene (usando checkSelfPermission()).

Una solución a corto plazo es reducir su nivel targetSdkVersionpor debajo de 23.

Pero, eventualmente, querrá actualizar su aplicación para usar el sistema de permisos de tiempo de ejecución.

Por ejemplo, esta actividad funciona con cinco permisos. Cuatro son permisos de tiempo de ejecución, aunque actualmente solo maneja tres (lo escribí antes de que WRITE_EXTERNAL_STORAGEse agregara a la lista de permisos de tiempo de ejecución).

/***
 Copyright (c) 2015 CommonsWare, LLC
 Licensed under the Apache License, Version 2.0 (the "License"); you may not
 use this file except in compliance with the License. You may obtain a copy
 of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
 by applicable law or agreed to in writing, software distributed under the
 License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
 OF ANY KIND, either express or implied. See the License for the specific
 language governing permissions and limitations under the License.

 From _The Busy Coder's Guide to Android Development_
 https://commonsware.com/Android
 */

package com.commonsware.android.permmonger;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
  private static final String[] INITIAL_PERMS={
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.READ_CONTACTS
  };
  private static final String[] CAMERA_PERMS={
    Manifest.permission.CAMERA
  };
  private static final String[] CONTACTS_PERMS={
      Manifest.permission.READ_CONTACTS
  };
  private static final String[] LOCATION_PERMS={
      Manifest.permission.ACCESS_FINE_LOCATION
  };
  private static final int INITIAL_REQUEST=1337;
  private static final int CAMERA_REQUEST=INITIAL_REQUEST+1;
  private static final int CONTACTS_REQUEST=INITIAL_REQUEST+2;
  private static final int LOCATION_REQUEST=INITIAL_REQUEST+3;
  private TextView location;
  private TextView camera;
  private TextView internet;
  private TextView contacts;
  private TextView storage;

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

    location=(TextView)findViewById(R.id.location_value);
    camera=(TextView)findViewById(R.id.camera_value);
    internet=(TextView)findViewById(R.id.internet_value);
    contacts=(TextView)findViewById(R.id.contacts_value);
    storage=(TextView)findViewById(R.id.storage_value);

    if (!canAccessLocation() || !canAccessContacts()) {
      requestPermissions(INITIAL_PERMS, INITIAL_REQUEST);
    }
  }

  @Override
  protected void onResume() {
    super.onResume();

    updateTable();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.actions, menu);

    return(super.onCreateOptionsMenu(menu));
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch(item.getItemId()) {
      case R.id.camera:
        if (canAccessCamera()) {
          doCameraThing();
        }
        else {
          requestPermissions(CAMERA_PERMS, CAMERA_REQUEST);
        }
        return(true);

      case R.id.contacts:
        if (canAccessContacts()) {
          doContactsThing();
        }
        else {
          requestPermissions(CONTACTS_PERMS, CONTACTS_REQUEST);
        }
        return(true);

      case R.id.location:
        if (canAccessLocation()) {
          doLocationThing();
        }
        else {
          requestPermissions(LOCATION_PERMS, LOCATION_REQUEST);
        }
        return(true);
    }

    return(super.onOptionsItemSelected(item));
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    updateTable();

    switch(requestCode) {
      case CAMERA_REQUEST:
        if (canAccessCamera()) {
          doCameraThing();
        }
        else {
          bzzzt();
        }
        break;

      case CONTACTS_REQUEST:
        if (canAccessContacts()) {
          doContactsThing();
        }
        else {
          bzzzt();
        }
        break;

      case LOCATION_REQUEST:
        if (canAccessLocation()) {
          doLocationThing();
        }
        else {
          bzzzt();
        }
        break;
    }
  }

  private void updateTable() {
    location.setText(String.valueOf(canAccessLocation()));
    camera.setText(String.valueOf(canAccessCamera()));
    internet.setText(String.valueOf(hasPermission(Manifest.permission.INTERNET)));
    contacts.setText(String.valueOf(canAccessContacts()));
    storage.setText(String.valueOf(hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)));
  }

  private boolean canAccessLocation() {
    return(hasPermission(Manifest.permission.ACCESS_FINE_LOCATION));
  }

  private boolean canAccessCamera() {
    return(hasPermission(Manifest.permission.CAMERA));
  }

  private boolean canAccessContacts() {
    return(hasPermission(Manifest.permission.READ_CONTACTS));
  }

  private boolean hasPermission(String perm) {
    return(PackageManager.PERMISSION_GRANTED==checkSelfPermission(perm));
  }

  private void bzzzt() {
    Toast.makeText(this, R.string.toast_bzzzt, Toast.LENGTH_LONG).show();
  }

  private void doCameraThing() {
    Toast.makeText(this, R.string.toast_camera, Toast.LENGTH_SHORT).show();
  }

  private void doContactsThing() {
    Toast.makeText(this, R.string.toast_contacts, Toast.LENGTH_SHORT).show();
  }

  private void doLocationThing() {
    Toast.makeText(this, R.string.toast_location, Toast.LENGTH_SHORT).show();
  }
}

(de este proyecto de muestra )

Para la función requestPermissions (), ¿los parámetros deberían ser "ACCESS_COARSE_LOCATION"? ¿O debería incluir el nombre completo "android.permission.ACCESS_COARSE_LOCATION"?

Usaría las constantes definidas en Manifest.permission, como se muestra arriba.

Además, ¿cuál es el código de solicitud?

Eso se le devolverá como el primer parámetro de onRequestPermissionsResult(), para que pueda distinguir una requestPermissions()llamada de otra.

CommonsWare
fuente
1
Para la función requestPermissions (), ¿los parámetros deberían ser "ACCESS_COARSE_LOCATION"? ¿O debería incluir el nombre completo "android.permission.ACCESS_COARSE_LOCATION"?
Jason Cromer
1
Gracias, esto eliminó el error. Sigo teniendo problemas para acceder a mi ubicación, ya que mi locationManager sigue devolviendo mi ubicación como nula, pero eso no es relevante para este error. ¡Gracias por tu solución!
Jason Cromer
@CommonsWare: ¿Qué quiere decir con "eventualmente"? Lo siento, no entiendo esa parte.
theapache64
1
@ theapache64: Algún día, algo hará que quieras establecer tu targetSdkVersionen 23 o más. En ese momento, deberá adoptar el sistema de permisos en tiempo de ejecución. Hasta que llegue ese momento, puede mantener su nivel targetSdkVersionpor debajo de 23 e ignorar los permisos de tiempo de ejecución.
CommonsWare
@CommonsWare: Ahora lo tengo. :)
theapache64
39

Mi solución simple es esta

if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED &&
        ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED) {
    googleMap.setMyLocationEnabled(true);
    googleMap.getUiSettings().setMyLocationButtonEnabled(true);
} else {
    Toast.makeText(this, R.string.error_permission_map, Toast.LENGTH_LONG).show();
}

o puede abrir el diálogo de permisos en algo así

} else {
   ActivityCompat.requestPermissions(this, new String[] {
      Manifest.permission.ACCESS_FINE_LOCATION, 
      Manifest.permission.ACCESS_COARSE_LOCATION }, 
      TAG_CODE_PERMISSION_LOCATION);
}
Vasil Valchev
fuente
las calles se mueven a otra parte hermano :(
Ashana.Jackol
2
agregue un cuadro de diálogo para agregar permisos en este "más", y estará listo para comenzar.
Vasil Valchev
De hecho, esta es sin duda la solución para Android 6. Cabe señalar que debe poner las solicitudes de permiso en el otro.
Keith Adler
Recibí este error con Target SDK como 22 y en Android 5.1 en el dispositivo S plus (GiONEE_WBL7511). Estoy confundido sobre por qué ocurrió este accidente. ¿Alguna pista? java.lang.SecurityException: el cliente debe tener permiso ACCESS_FINE_LOCATION para solicitar ubicaciones PRIORITY_HIGH_ACCURACY.
arpitgoyal2008
5

CAUSA: "A partir de Android 6.0 (nivel de API 23), los usuarios otorgan permisos a las aplicaciones mientras la aplicación se está ejecutando, no cuando la instalan". En este caso, "ACCESS_FINE_LOCATION" es un "permiso peligroso y por esa razón, obtienes este 'java.lang.SecurityException: el proveedor de ubicación" gps "requiere permiso ACCESS_FINE_LOCATION.' error ( https://developer.android.com/training/permissions/requesting.html ).

SOLUCIÓN: Implementar el código proporcionado en https://developer.android.com/training/permissions/requesting.html bajo los encabezados "Solicite los permisos que necesita" y "Maneje la respuesta de solicitud de permisos".

Jaime Montoya
fuente