Disable / Check for Mock Location (prevent gps spoofing)
Asked Answered
D

10

104

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?

Dearborn answered 30/7, 2011 at 0:14 Comment(2)
I think he's asking about the Location Spoofing function available in the DDMS view in Eclipse.Troat
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).Dearborn
R
147

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.

Flutter Update: Use Geolocator and check Position object's isMocked property.

Runyon answered 27/5, 2013 at 16:4 Comment(13)
Very interesting observations. Especially last one +1 for sharing this.Crept
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.Nelrsa
Also your app must have android.permission.ACCESS_MOCK_LOCATION permission for removeTestProvider to work, which I think is the biggest disadvantage.Nelrsa
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()Sequential
In some samsung devices Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ALLOW_MOCK_LOCATION) returns "1" even if mock locations are not setStiltner
@Nelrsa ACCESS_MOCK_LOCATION is deprecated, how you using removeTestProvider method now ?Balderas
@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.Nelrsa
@Nelrsa I asked a question which refers indirectly this answer and also your above comment. To understand my question, i needed to use removeTestProvider method but i'm out of luck. Because it needs permission and the permission is deprecated.Balderas
@Mistaken Android 6.0 onward, this will always return '1 ' :Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ALLOW_MOC‌​K_LOCATION) For Android 6.0 you can use something like this: AppOpsManager opsManager = (AppOpsManager) LockTimer.getSystemService(Context.APP_OPS_SERVICE); isMockLocation = (opsManager.checkOp(AppOpsManager.OPSTR_MOCK_LOCATION, android.os.Process.myUid(), BuildConfig.APPLICATION_ID)== AppOpsManager.MODE_ALLOWED);Gerdes
Some of you tried to handle this fake locations from apps like Fake GPS Go? i tried with a NMEA listener too, but isn't works! also the method isFromMockProvider() doesnt works wit this app, any idea on how to handle this, i am reading a lot of post for a possible solutionBaeda
@Nelrsa How do you find the whitelist of the legitimate apps. On OnePlus 5, a weather app is showing the access to MOCK_LOCATION. How do you do it?Pled
As I mentioned in my previous comment, I gave up on a whitelist and instead used a blacklist of popular location spoofing apps found on Play Store.Nelrsa
@Nelrsa It would be great if you could wrap your solution into an open source library and give options for extending the blacklist and whitelistBromine
S
54

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");
}
Seicento answered 11/1, 2016 at 16:6 Comment(3)
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");Crumhorn
You're welcome. Thanks for posting a more modern answer! Really this is the correct answer as of today.Crumhorn
How we can do it without "location" object for SDK above 18?Flycatcher
D
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;
Dearborn answered 14/9, 2011 at 3:24 Comment(7)
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..Dearborn
Furthermore, Fake GPS doesn't require the mock location setting on rooted devices.Dowell
Could always check to verify that the fake.gps app is not installed :)Dearborn
You can use return !x instead of if(x) return false; else return true.Ulcer
In fact, Fake location will change mock location setting even on rooted devices.Michellmichella
How we can do above Mock location enable check for Marshmallow OS?Flycatcher
I'm using o this code and I'm pretty sure mock location is enable and an app use of mock location, but this code always return true. why?Horsecar
V
27

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.

Verdure answered 5/6, 2016 at 19:37 Comment(7)
You should briefly summarise the linked blog post (where does isFromMockProvider fail).Crumhorn
@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.Verdure
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)Crumhorn
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!Verdure
Thank you! For me this is now much clearer and definitely adds to the discussion. +1Crumhorn
What if user do not have Google Play Service installed?Marinelli
Good Job! :) Thanks!Wolfie
S
7

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.

Smuggle answered 2/7, 2012 at 2:8 Comment(1)
Thanks, that is a pretty good idea. I may have to look into that. ThanksDearborn
M
4

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;
    }
Merri answered 10/3, 2018 at 10:54 Comment(4)
This is a much better version of the isMockLocationEnabled method above.Lioness
AppOpsManager.checkOp() throws SecurityException If the app has been configured to crash on this op. Like: java.lang.SecurityException: packagename from uid 11151 not allowed to perform MOCK_LOCATION. This method is about to detect "if your app can mock locations". But not "if received locations are mocked".Bergamot
@Bergamot by "if your app can mock locations" you mean if the current app has permission to mock locations, right?Rosalia
@VictorLaerte I've last a context of a topic :/ Right. But the question was: "how to detect if a received location is mocked or is from a mock provider". Or how to ignore fake locations.Bergamot
E
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
    }
Eileen answered 7/6, 2017 at 18:50 Comment(0)
T
2

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();
}
Towns answered 21/2, 2018 at 19:13 Comment(0)
N
1

Below approach is working for me getting proper detection of mock location

@Override
public void onLocationChanged (Location location){
    boolean isMockLocation = location.isFromMockProvider();
}
Nall answered 9/10, 2021 at 13:2 Comment(0)
A
0

Paste this in your activity/where you want to validate fake/mock gps

  try {
        if (areThereMockPermissionApps(mContext)) {
            Log.e(TAG, " - " + "Yup its use fake gps");
            List<String> mFakeList = new ArrayList<>();
            mFakeList = getListOfFakeLocationAppsInstalled(mContext); // this will return the fake app list
            for (int a = 0; a < mFakeList.size(); a++) { 
                Log.e(TAG, mFakeList.size() + "  - " + "NameList ----- " + mFakeList.get(a));
            }
        } else
            Log.e(TAG, " - " + "Nope its not use fake gps");
  } catch (Exception w) {
        w.printStackTrace();
  }

Here you can get the list of installed fake/mock app in your device.

    private List<String> getListOfFakeLocationAppsInstalled(Context context) {
    List<String> fakeApps = new ArrayList<>();
    try {
        List<String> runningApps = new ArrayList<>();
        final PackageManager pm = getPackageManager();
        List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);

        for (ApplicationInfo packageInfo : packages) {
            runningApps.add(packageInfo.packageName);
        } // the getLaunchIntentForPackage returns an intent that you can use with startActivity()

        for (String app : runningApps) {
            if (!isSystemPackage(context, app) && hasAppPermission(context, app, "android.permission.ACCESS_MOCK_LOCATION")) {
                fakeApps.add(getApplicationName(context, app));
            }
        }
    } catch (Exception w) {
        w.printStackTrace();
    }
    return fakeApps;
}

Paste this method in your Helper/same class

   public static boolean areThereMockPermissionApps(Context context) {
    int count = 0;
    try {
        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 (PackageManager.NameNotFoundException e) {
                Log.e("MockDeductionAgilanbu", "Got exception --- " + e.getMessage());
            }
        }
    } catch (Exception w) {
        w.printStackTrace();
    }
    if (count > 0)
        return true;
    return false;
}
Ailsa answered 15/4, 2021 at 6:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.