Błąd „gps wymaga ACCESS_FINE_LOCATION” w systemie Android, mimo że zawiera to mój plik manifestu

104

Za każdym razem, gdy uruchamiam aplikację, mój SecurityException jest wyrzucany, a błąd z debugera odczytuje się następująco:

java.lang.SecurityException: dostawca lokalizacji „gps” wymaga uprawnienia ACCESS_COARSE_LOCATION lub ACCESS_FINE_LOCATION.

Wydaje się, że to zwykły błąd, jednak mój plik manifestu jest całkowicie poprawny. Oto jest, a tutaj jest również mój kod MapActivity:

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

Moja aktywność:

    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
źródło
Na jakiej wersji Androida to testujesz?
CommonsWare
4
Niepowiązane, ale jeśli zażądasz dokładnej lokalizacji, nie musisz prosić o przybliżoną. To jest wliczone w cenę.
joey_g216,

Odpowiedzi:

136

ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATIONi WRITE_EXTERNAL_STORAGEwszystkie są częścią systemu uprawnień wykonawczych Androida 6.0 . Oprócz posiadania ich w manifeście, tak jak robisz, musisz również zażądać ich od użytkownika w czasie wykonywania (przy użyciu requestPermissions()) i sprawdzić, czy je masz (używając checkSelfPermission()).

Krótkoterminowym obejściem jest obniżenie poziomu targetSdkVersionponiżej 23.

Ostatecznie jednak będziesz chciał zaktualizować swoją aplikację, aby korzystała z systemu uprawnień wykonawczych.

Na przykład to działanie działa z pięcioma uprawnieniami. Cztery to uprawnienia WRITE_EXTERNAL_STORAGEuruchomieniowe , chociaż obecnie obsługuje tylko trzy (napisałem to wcześniej, gdy został dodany do listy uprawnień uruchomieniowych).

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

(z tego przykładowego projektu )

W przypadku funkcji requestPermissions () parametry powinny mieć tylko wartość „ACCESS_COARSE_LOCATION”? A może powinienem podać pełną nazwę „android.permission.ACCESS_COARSE_LOCATION”?

Użyłbym stałych zdefiniowanych na Manifest.permission, jak pokazano powyżej.

Jaki jest również kod żądania?

Zostanie on przekazany z powrotem jako pierwszy parametr do onRequestPermissionsResult(), abyś mógł odróżnić jedno requestPermissions()wywołanie od drugiego.

CommonsWare
źródło
1
W przypadku funkcji requestPermissions () parametry powinny mieć tylko wartość „ACCESS_COARSE_LOCATION”? A może powinienem podać pełną nazwę „android.permission.ACCESS_COARSE_LOCATION”?
Jason Cromer
1
Dziękuję, to pozbyło się błędu. Nadal mam problemy z uzyskaniem dostępu do mojej lokalizacji, ponieważ mój locationManager zwraca moją lokalizację jako zerową, ale nie ma to związku z tym błędem. Dziękuję za rozwiązanie!
Jason Cromer
@CommonsWare: Co masz na myśli mówiąc „w końcu”? Przepraszam, nie rozumiem tej części.
theapache64
1
@ theapache64: Któregoś dnia coś spowoduje, że zechcesz ustawić targetSdkVersion23 lub więcej. W tym momencie będziesz musiał zastosować system uprawnień wykonawczych. Dopóki ten czas nie nadejdzie, możesz zachować targetSdkVersionponiżej 23 lat i zignorować uprawnienia wykonawcze.
CommonsWare
@CommonsWare: Teraz mam to. :)
theapache64
39

Moje proste rozwiązanie jest takie

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

lub możesz otworzyć okno dialogowe uprawnień w innym, takim jak ten

} else {
   ActivityCompat.requestPermissions(this, new String[] {
      Manifest.permission.ACCESS_FINE_LOCATION, 
      Manifest.permission.ACCESS_COARSE_LOCATION }, 
      TAG_CODE_PERMISSION_LOCATION);
}
Vasil Valchev
źródło
laways przenosi się do innej części brachu :(
Ashana.Jackol
2
dodaj okno dialogowe, aby dodać uprawnienia w tym „innym” i jesteś gotowy.
Vasil Valchev,
W rzeczywistości jest to bez wątpienia poprawka dla Androida 6. Należy zauważyć, że prośby o pozwolenie należy umieścić w innym miejscu.
Keith Adler,
Otrzymałem ten błąd z docelowym SDK jako 22 i na Androidzie 5.1 na urządzeniu S plus (GiONEE_WBL7511). Nie wiem, dlaczego doszło do tej katastrofy. Jakieś wskazówki? java.lang.SecurityException: klient musi mieć uprawnienie ACCESS_FINE_LOCATION, aby zażądać lokalizacji PRIORITY_HIGH_ACCURACY.
arpitgoyal2008
5

PRZYCZYNA: „Począwszy od systemu Android 6.0 (poziom API 23), użytkownicy przyznają uprawnienia aplikacjom, gdy aplikacja jest uruchomiona, a nie podczas jej instalowania”. W tym przypadku „ACCESS_FINE_LOCATION” jest „niebezpiecznym uprawnieniem” iz tego powodu otrzymujesz ten „java.lang.SecurityException: dostawca lokalizacji„ gps ”wymaga uprawnienia ACCESS_FINE_LOCATION”. błąd ( https://developer.android.com/training/permissions/requesting.html ).

ROZWIĄZANIE: Wdrożenie kodu podanego na https://developer.android.com/training/permissions/requesting.html pod nagłówkami „Poproś o uprawnienia, których potrzebujesz” i „Obsługuj prośbę o pozwolenie na odpowiedź”.

Jaime Montoya
źródło