78

Looking to find the best way to prevent / detect GPS spoofing on Android. Any suggestions on how this is accomplished, and what can be done to stop it? I am guessing the user has to turn on mock locations to spoof GPS, if this is done, then they can spoof GPS?

I guess I would need to just detect if Mock Locations are enabled? Any other suggestions?

  • 2
    I think he's asking about the Location Spoofing function available in the DDMS view in Eclipse. – Shawn Walton Jul 30 '11 at 0:54
  • 2
    I have a location based game which I don't want people cheating on, so I wand to block spoofing, my understanding is it can happen one of two ways.. Having mock locations enabled, and building a custom image that does low level spoofing and disregards the spoofing setting in the settings app. Trying to find the Settings.System Provider for MockLocations, or looking to see if it gets enabled (with a listener in the middle of the app). – Chrispix Jul 30 '11 at 1:46
111

I have done some investigation and sharing my results here,this may be useful for others.

First, we can check whether MockSetting option is turned ON

public static boolean isMockSettingsON(Context context) {
    // returns true if mock location enabled, false if not enabled.
    if (Settings.Secure.getString(context.getContentResolver(),
                                Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
        return false;
    else
        return true;
}

Second, we can check whether are there other apps in the device, which are using android.permission.ACCESS_MOCK_LOCATION (Location Spoofing Apps)

public static boolean areThereMockPermissionApps(Context context) {
    int count = 0;

    PackageManager pm = context.getPackageManager();
    List<ApplicationInfo> packages =
        pm.getInstalledApplications(PackageManager.GET_META_DATA);

    for (ApplicationInfo applicationInfo : packages) {
        try {
            PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName,
                                                        PackageManager.GET_PERMISSIONS);

            // Get Permissions
            String[] requestedPermissions = packageInfo.requestedPermissions;

            if (requestedPermissions != null) {
                for (int i = 0; i < requestedPermissions.length; i++) {
                    if (requestedPermissions[i]
                        .equals("android.permission.ACCESS_MOCK_LOCATION")
                        && !applicationInfo.packageName.equals(context.getPackageName())) {
                        count++;
                    }
                }
            }
        } catch (NameNotFoundException e) {
            Log.e("Got exception " , e.getMessage());
        }
    }

    if (count > 0)
        return true;
    return false;
}

If both above methods, first and second are true, then there are good chances that location may be spoofed or fake.

Now, spoofing can be avoided by using Location Manager's API.

We can remove the test provider before requesting the location updates from both the providers (Network and GPS)

LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);

try {
    Log.d(TAG ,"Removing Test providers")
    lm.removeTestProvider(LocationManager.GPS_PROVIDER);
} catch (IllegalArgumentException error) {
    Log.d(TAG,"Got exception in removing test  provider");
}

lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, locationListener);

I have seen that removeTestProvider(~) works very well over Jelly Bean and onwards version. This API appeared to be unreliable till Ice Cream Sandwich.

  • Thanks for sharing, will take a look into it.. – Chrispix May 27 '13 at 16:08
  • 2
    Note about the removeTestProvider method. If you allow the Location Manager to work in background the user can go to mock app and restart mocking location. Your location manager will then start receiving mock locations until you call removeTestProvider again. – Timur_C Mar 17 '15 at 17:35
  • 3
    Also your app must have android.permission.ACCESS_MOCK_LOCATION permission for removeTestProvider to work, which I think is the biggest disadvantage. – Timur_C Mar 17 '15 at 17:41
  • 14
    thanks for the answer! just a point: in Android 6.0 ALLOW_MOCK_LOCATION is deprecated. And actually there's no checkbox for mock location as well. One can check if location is fake or not right from location object: location.isFromMockProvider() – Silwester Jan 20 '16 at 13:51
  • 2
    @Blackkara I didn't use it after all. I used a customized combination of isMockSettingsON(), Location.isFromMockProvider() and areThereMockPermissionApps() with a black list of apps. There are a lot of preinstalled system apps with ACCESS_MOCK_LOCATION permission, for example on HTC and Samsung devices. A whitelist of all legitimate apps would be better but a black list of most popular location spoofing apps worked well in my case. And I also checked if the device was rooted. – Timur_C May 12 '16 at 23:11
34

It seems that the only way to do this is to prevent Location Spoofing preventing MockLocations. The down side is there are some users who use Bluetooth GPS devices to get a better signal, they won't be able to use the app as they are required to use the mock locations.

To do this, I did the following :

// returns true if mock location enabled, false if not enabled.
if (Settings.Secure.getString(getContentResolver(),
       Settings.Secure.ALLOW_MOCK_LOCATION).equals("0")) 
       return false; 
       else return true;
  • 4
    This is not fool proof though. Users on a non-rooted device can still set a mock location, w/ a time in the future, then disable mock locations, and the mock location is still active. Even worse, they can call the mock location the same provider name as Network/Gps and it apparently pulls from that.. – Chrispix Jan 15 '12 at 4:10
  • 2
    Furthermore, Fake GPS doesn't require the mock location setting on rooted devices. – Paul Lammertsma Mar 7 '12 at 13:21
  • Could always check to verify that the fake.gps app is not installed :) – Chrispix Mar 12 '12 at 19:38
  • 10
    You can use return !x instead of if(x) return false; else return true. – CodesInChaos Aug 12 '14 at 13:10
  • In fact, Fake location will change mock location setting even on rooted devices. – PageNotFound Aug 19 '14 at 13:02
33

Since API 18, the object Location has the method .isFromMockProvider() so you can filter out fake locations.

If you want to support versions before 18, it is possible to use something like this:

boolean isMock = false;
if (android.os.Build.VERSION.SDK_INT >= 18) {
    isMock = location.isFromMockProvider();
} else {
    isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
}
  • I'm fairly sure your second expression is backwards (return true when it should return false). I think it should be: isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"); – AjahnCharles Jul 18 '16 at 18:48
  • You are right, thank you. – Fernando Jul 19 '16 at 20:05
  • 2
    You're welcome. Thanks for posting a more modern answer! Really this is the correct answer as of today. – AjahnCharles Jul 20 '16 at 22:56
  • 1
    How we can do it without "location" object for SDK above 18? – Ajit Sharma Jan 19 '17 at 8:11
23

Stumbled upon this thread a couple years later. In 2016, most Android devices will have API level >= 18 and should thus rely on Location.isFromMockProvider() as pointed out by Fernando.

I extensively experimented with fake/mock locations on different Android devices and distros. Unfortunately .isFromMockProvider() is not 100% reliable. Every once in a while, a fake location will not be labeled as mock. This seems to be due to some erroneous internal fusion logic in the Google Location API.

I wrote a detailed blog post about this, if you want to learn more. To summarize, if you subscribe to location updates from the Location API, then switch on a fake GPS app and print the result of each Location.toString() to the console, you will see something like this:

enter image description here

Notice how, in the stream of location updates, one location has the same coordinates as the others, but is not flagged as a mock and has a much poorer location accuracy.

To remedy this problem, I wrote a utility class that will reliably suppress Mock locations across all modern Android versions (API level 15 and up):

LocationAssistant - Hassle-free location updates on Android

Basically, it "distrusts" non-mock locations that are within 1km of the last known mock location and also labels them as a mock. It does this until a significant number of non-mock locations have arrived. The LocationAssistant can not only reject mock locations, but also unburdens you from most of the hassle of setting up and subscribing to location updates.

To receive only real location updates (i.e. suppress mocks), use it as follows:

public class MyActivity extends Activity implements LocationAssistant.Listener {

    private LocationAssistant assistant;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // You can specify a different accuracy and interval here.
        // The last parameter (allowMockLocations) must be 'false' to suppress mock locations.  
        assistant = new LocationAssistant(this, this, LocationAssistant.Accuracy.HIGH, 5000, false);
    }

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

    @Override
    protected void onPause() {
        assistant.stop();
        super.onPause();
    }

    @Override
    public void onNewLocationAvailable(Location location) {
        // No mock locations arriving here
    }

    ...
}

onNewLocationAvailable() will now only be invoked with real location info. There are some more listener methods you need to implement, but in the context of your question (how to prevent GPS spoofing) this is basically it.

Of course, with a rooted OS you can still find ways of spoofing location info that are impossible for normal apps to detect.

  • You should briefly summarise the linked blog post (where does isFromMockProvider fail). – AjahnCharles Jul 18 '16 at 18:52
  • @CodeConfident - thanks for the remark! Not sure what I should add though. The second paragraph of my answer is the summary of the blog post. .isFromMockProvider fails sporadically and unpredictably. In the article I just describe in more detail the steps I took to discover and remedy this. – KlaasNotFound Jul 20 '16 at 22:52
  • Well I was forced to jump to your article to understand which to me feels like it runs against the intention of SO. My best suggestion would be: (1) insert your pic that shows the dodgy location (not flagged as a mock) and (2) quickly note your logic for eliminating them (ignore within 1km of a mock) – AjahnCharles Jul 20 '16 at 23:06
  • Ok, gotcha. I think in the context of the OP, the specifics of why .isFromMockProvider() is unreliable are not too relevant. But I will attempt to add the details you mentioned for the bigger picture. Thanks for the feedback! – KlaasNotFound Jul 20 '16 at 23:17
  • 1
    What if user do not have Google Play Service installed? – Yuriy Chernyshov Aug 9 '16 at 17:59
6

If you happened to know the general location of cell towers, you could check to see if the current cell tower matches the location given (within an error margin of something large, like 10 or more miles).

For example, if your app unlocks features only if the user is in a specific location (your store, for example), you could check gps as well as cell towers. Currently, no gps spoofing app also spoofs the cell towers, so you could see if someone across the country is simply trying to spoof their way into your special features (I'm thinking of the Disney Mobile Magic app, for one example).

This is how the Llama app manages location by default, since checking cell tower ids are much less battery intensive than gps. It isn't useful for very specific locations, but if home and work are several miles away, it can distinguish between the two general locations very easily.

Of course, this would require the user to have a cell signal at all. And you would have to know all the cell towers ids in the area --on all network providers-- or you would run the risk of a false negative.

  • 1
    Thanks, that is a pretty good idea. I may have to look into that. Thanks – Chrispix Jul 9 '12 at 19:49
3

try this code its very simple and usefull

  public boolean isMockLocationEnabled() {
        boolean isMockLocation = false;
        try {
            //if marshmallow
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                AppOpsManager opsManager = (AppOpsManager) getApplicationContext().getSystemService(Context.APP_OPS_SERVICE);
                isMockLocation = (opsManager.checkOp(AppOpsManager.OPSTR_MOCK_LOCATION, android.os.Process.myUid(), BuildConfig.APPLICATION_ID)== AppOpsManager.MODE_ALLOWED);
            } else {
                // in marshmallow this will always return true
                isMockLocation = !android.provider.Settings.Secure.getString(getApplicationContext().getContentResolver(), "mock_location").equals("0");
            }
        } catch (Exception e) {
            return isMockLocation;
        }
        return isMockLocation;
    }
  • This is a much better version of the isMockLocationEnabled method above. – setzamora Mar 15 '18 at 18:17
2

This scrip is working for all version of android and i find it after many search

LocationManager locMan;
    String[] mockProviders = {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER};

    try {
        locMan = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        for (String p : mockProviders) {
            if (p.contentEquals(LocationManager.GPS_PROVIDER))
                locMan.addTestProvider(p, false, false, false, false, true, true, true, 1,
                        android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_HIGH);
            else
                locMan.addTestProvider(p, false, false, false, false, true, true, true, 1,
                        android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_LOW);

            locMan.setTestProviderEnabled(p, true);
            locMan.setTestProviderStatus(p, android.location.LocationProvider.AVAILABLE, Bundle.EMPTY,
                    java.lang.System.currentTimeMillis());
        }
    } catch (Exception ignored) {
        // here you should show dialog which is mean the mock location is not enable
    }
1

You can add additional check based on cell tower triangulation or Wifi Access Points info using Google Maps Geolocation API

The simplest way to get info about CellTowers

final TelephonyManager telephonyManager = (TelephonyManager) appContext.getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = telephonyManager.getNetworkOperator();
int mcc = Integer.parseInt(networkOperator.substring(0, 3));
int mnc = Integer.parseInt(networkOperator.substring(3));
String operatorName = telephonyManager.getNetworkOperatorName();
final GsmCellLocation cellLocation = (GsmCellLocation) telephonyManager.getCellLocation();
int cid = cellLocation.getCid();
int lac = cellLocation.getLac();

You can compare your results with site

To get info about Wifi Access Points

final WifiManager mWifiManager = (WifiManager) appContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);

if (mWifiManager != null && mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {

    // register WiFi scan results receiver
    IntentFilter filter = new IntentFilter();
    filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);

    BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                List<ScanResult> results = mWifiManager.getScanResults();//<-result list
            }
        };

        appContext.registerReceiver(broadcastReceiver, filter);

        // start WiFi Scan
        mWifiManager.startScan();
}

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.