Detecting Mock Location Not Working (Android)

2019-09-02 07:14发布

问题:

I'm trying to set some protection against people using mock locations to manipulate my app. I realise that it's impossible to prevent 100%... I'm just trying to do what I can.

The app uses Google location services (part of play services) in its main activity.

The onLocationChanged() method is:

public void onLocationChanged(Location location) {
    this.mCurrentLocation = location;
    if (Build.VERSION.SDK_INT > 17 && mCurrentLocation.isFromMockProvider()) {
        Log.i(TAG, "QQ Location is MOCK");
        // TODO: add code to stop app
        // otherwise, currently, location will never be updated and app will never start
    } else {
        Double LAT = mCurrentLocation.getLatitude();
        Double LON = mCurrentLocation.getLongitude();
        if ((LAT.doubleValue() > 33.309171) || (LAT.doubleValue() < 33.226442) || (LON.doubleValue() < -90.790165) || (LON.doubleValue() > -90.707081)) {
            buildAlertMessageOutOfBounds();
        } else if (waiting4FirstLocationUpdate) {
            Log.i(TAG, "YYY onLocationChanged() determines this is the FIRST update.");
            waiting4FirstLocationUpdate = false;
            setContentView(R.layout.activity_main);
            startDisplayingLists();

        }
    }
}

The location services work perfectly and all is well with the app in general, but when I run the app in an emulator with Android Studio (Nexus One API 23), and I set the location using extended controls (mock), the app just continues to work as normal, and so it seems that the condition:

if (Build.VERSION.SDK_INT > 17 && mCurrentLocation.isFromMockProvider())

Is returning false.

This doesn't make any sense to me. Does anyone know why this would happen?

Thanks!

回答1:

The short answer: .isFromMockProvider is unreliable. Some fake locations are not properly detected as such. I have spent an extensive amount of time researching this and written a detailed blog post about it.

I also spent time to find a solution to reliably suppress mock locations across all recent/relevant Android versions and made a utility class, called the LocationAssistant, that does the job.

In a nutshell (using the aforementioned LocationAssistant):

  1. Set up permissions in your manifest and Google Play Services in your gradle file.

  2. Copy the file LocationAssistant.java to your project.

  3. In the onCreate() method of your Activity, instantiate a LocationAssistant with the desired parameters. For example, to receive high-accuracy location updates roughly every 5 seconds and reject mock locations, call new LocationAssistant(this, this, LocationAssistant.Accuracy.HIGH, 5000, false). The last argument specifies that mock locations shouldn't be allowed.

  4. Start/stop the assistant with your Activity and notify it of permission/location settings changes (see the documentation for details).

  5. Enjoy location updates in onNewLocationAvailable(Location location). If you chose to reject mock locations, the callback is only invoked with non-mock locations.

There are some more methods to implement, but essentially this is it. Obviously, there are some ways to get around mock provider detection with rooted devices, but on stock, non-rooted devices the rejection should work reliably.