Android “gps requer ACCESS_FINE_LOCATION” erro, embora meu arquivo de manifesto contenha este

104

Cada vez que executo o aplicativo, minha SecurityException é lançada e o erro do depurador é o seguinte:

java.lang.SecurityException: o provedor de localização "gps" requer a permissão ACCESS_COARSE_LOCATION ou ACCESS_FINE_LOCATION.

Isso parece um erro simples, no entanto, meu arquivo de manifesto está totalmente correto. Aqui está, e aqui está meu código MapActivity também:

<?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>

Minha atividade:

    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
fonte
Em qual versão do Android você está testando isso?
CommonsWare
4
Não relacionado, mas se você solicitar uma localização precisa, não é necessário solicitar grosseiro. Está incluído.
joey_g216

Respostas:

136

ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATIONe WRITE_EXTERNAL_STORAGEfazem parte do sistema de permissão de tempo de execução do Android 6.0 . Além de tê-los no manifesto conforme você os faz, você também deve solicitá-los ao usuário em tempo de execução (usando requestPermissions()) e ver se os possui (usando checkSelfPermission()).

Uma solução alternativa a curto prazo é diminuir seu valor para targetSdkVersionmenos de 23 anos.

Mas, eventualmente, você desejará atualizar seu aplicativo para usar o sistema de permissão de tempo de execução.

Por exemplo, esta atividade funciona com cinco permissões. Quatro são permissões de tempo de execução, embora atualmente esteja tratando apenas de três (eu escrevi antes de WRITE_EXTERNAL_STORAGEser adicionado à lista de permissões de tempo de execução).

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

( deste projeto de amostra )

Para a função requestPermissions (), os parâmetros devem ser apenas "ACCESS_COARSE_LOCATION"? Ou devo incluir o nome completo "android.permission.ACCESS_COARSE_LOCATION"?

Eu usaria as constantes definidas em Manifest.permission, conforme mostrado acima.

Além disso, qual é o código de solicitação?

Isso será passado de volta para você como o primeiro parâmetro para onRequestPermissionsResult(), para que você possa diferenciar uma requestPermissions()chamada da outra.

CommonsWare
fonte
1
Para a função requestPermissions (), os parâmetros devem ser apenas "ACCESS_COARSE_LOCATION"? Ou devo incluir o nome completo "android.permission.ACCESS_COARSE_LOCATION"?
Jason Cromer
1
Obrigado, isso eliminou o erro. Ainda estou tendo problemas para acessar minha localização, pois meu locationManager continua retornando minha localização como nula, mas isso não é relevante para este bug. Obrigado pela sua solução!
Jason Cromer
@CommonsWare: O que você quer dizer com 'eventualmente'? Desculpe, não entendi essa parte.
theapache64 de
1
@ theapache64: Algum dia, algo fará com que você queira definir o seu targetSdkVersionpara 23 ou mais. Nesse ponto, você precisará adotar o sistema de permissão de tempo de execução. Até esse momento chegar, você é bem-vindo para manter seu targetSdkVersionabaixo de 23 e ignorar as permissões de tempo de execução.
CommonsWare
@CommonsWare: Agora entendi. :)
theapache64 de
39

Minha solução simples é 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();
}

ou você pode abrir a caixa de diálogo de permissão em outro lugar como este

} else {
   ActivityCompat.requestPermissions(this, new String[] {
      Manifest.permission.ACCESS_FINE_LOCATION, 
      Manifest.permission.ACCESS_COARSE_LOCATION }, 
      TAG_CODE_PERMISSION_LOCATION);
}
Vasil Valchev
fonte
laways move-se para outra parte mano :(
Ashana.Jackol
2
adicione caixa de diálogo para adicionar permissões neste "outro" e você está pronto para ir.
Vasil Valchev
Esta é, sem dúvida, a correção para o Android 6. Deve-se notar que você deve colocar as solicitações de permissão no else.
Keith Adler
Recebi este erro com o Target SDK como 22 e no Android 5.1 no dispositivo S plus (GiONEE_WBL7511). Estou confuso sobre o motivo desse acidente. Alguma pista? java.lang.SecurityException: o cliente deve ter permissão ACCESS_FINE_LOCATION para solicitar localizações PRIORITY_HIGH_ACCURACY.
arpitgoyal2008,
5

CAUSA: "A partir do Android 6.0 (API de nível 23), os usuários concedem permissões aos aplicativos enquanto o aplicativo está em execução, não quando o instalam." Nesse caso, "ACCESS_FINE_LOCATION" é uma "permissão perigosa e, por esse motivo, você obtém esta 'java.lang.SecurityException: provedor de localização" gps "requer permissão ACCESS_FINE_LOCATION.' erro ( https://developer.android.com/training/permissions/requesting.html ).

SOLUÇÃO: Implementar o código fornecido em https://developer.android.com/training/permissions/requesting.html sob os cabeçalhos "Solicite as permissões de que precisa" e "Tratar a resposta de solicitação de permissões".

Jaime Montoya
fonte