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!
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):
Set up permissions in your manifest and Google Play Services in your gradle file.
Copy the file LocationAssistant.java to your project.
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.
Start/stop the assistant with your Activity and notify it of permission/location settings changes (see the documentation for details).
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.
Related
Google's FusedLocationProviderApi for Android was recently deprecated within the past few months, with FusedLocationProviderClient being its successor so I recently updated the location APIs used in my client's app to use the new ones.
Every time onLocationAvailability is fired in LocationCallback I notify the user when locationAvailability.isLocationAvailable() returns false, but it appears that this condition occurs more often than I expected on some devices. I run these location updates inside a foreground Service and it is crucial that these location updates remain consistent. Is there a way to determine the cause of this failure so
We don't indicate any false positives to the end-user
We can try to fix the issue or at least report to the end-user what they should do?
It appears to me that either the deprecated APIs provide more insight into these issues since it was used in conjunction with GoogleApiClient or perhaps I'm missing some smaller details.
I went through the same issue. And after three days of trying things out, I got to work on it out.
I also had to collect a location in a foreground state like you, and if the foreground service was destroyed, I had to unregister.
The first mistake I made was not to guarantee that removeLocationUpdates would be run on the same thread as the requestLocationUpdates. Actually, it doesn't have to be the same thread, but after a requestLocationUpdates, you must call removeLocationUpdates to make the next requestLocationUpdates valid. To ensure this, it is much easier to work on the same thread.
For example:
private fun FusedLocationProviderClient.requestLocation(
request: LocationRequest
): Single<LocationResult> {
return Single.create<LocationResult> { emitter ->
requestLocationUpdates(request, object : LocationCallback() {
override fun onLocationResult(result: LocationResult?) {
removeLocationUpdates(object : LocationCallback() {})
.addOnCompleteListener {
if (emitter.isDisposed) {
info("onLocationResult called after disposing.")
return#addOnCompleteListener
}
if (result != null && result.locations.isNotEmpty()) {
onSuccess(result)
} else {
onError(RuntimeException("Invalid location result"))
}
}
}
private fun onError(error: Exception) {
if (!emitter.isDisposed) {
emitter.onError(error)
}
}
private fun onSuccess(item: LocationResult) {
if (!emitter.isDisposed) {
emitter.onSuccess(item)
}
}
}, Looper.getMainLooper())
}
}
As the code suggests, I have attracted Single's emitter to the addOnCompleteListener in removeLocationUpdates to ensure the call of removeLocationUpdates behind the requestLocationUpdates. Without RxJava, of course, it would be easier to implement.
The second mistake I made was the wrong interval setting in LocationRequest. According to the doc:
This method sets the rate in milliseconds at which your app prefers to receive location updates. Note that the location updates may be somewhat faster or slower than this rate to optimize for battery usage, or there may be no updates at all (if the device has no connectivity, for example).
The explanation is unkind but ultimately, if you call requestLocationUpdates once, you must have a Location update event triggered by interval before the next requestLocationUpdates. Finding this bug was the hardest.
The third mistake I made was to set the wrong priority in LocationRequest. In API 10 and below, it was not PRIORITY_BALANCED_POWER_ACCURACY but it was resolved by using PRIORITY_HIGH_ACCURACY. In this case, I have only tested on the emulator, so the actual device may have different results. I guess PRIORITY_BALANCED_POWER_ACCURACY doesn't seem to work properly because the emulator doesn't provide Bluetooth hardware.
So my LocationRequest looks like:
LocationRequest.apply {
priority = PRIORITY_HIGH_ACCURACY
interval = 10000L
}
I hope the three mistakes and solutions I made is helpful to you!
As official documentation says:
onLocationAvailability Called when there is a change in the
availability of location data. When isLocationAvailable() returns
false you can assume that location will not be returned in
onLocationResult(LocationResult) until something changes in the
device's settings or environment. Even when isLocationAvailable()
returns true the onLocationResult(LocationResult) may not always be
called regularly, however the device location is known and both the
most recently delivered location and getLastLocation(GoogleApiClient)
will be reasonably up to date given the hints specified by the active
LocationRequests.
So this method does not provide information about reason.
We don't indicate any false positives to the end-user
Currently I just ignore result of this method because it returns false too often, and then again true, and so on.
We can try to fix the issue or at least report to the end-user what they should do?
Check if Location Services are enabled (using LocationManager.isProviderEnabled())
Check if you have permissions, request them if needed (docs)
I filter false positives of locationAvailability.isLocationAvailable() by calling this piece of code, which checks if location service is enabled.
fun isLocationEnabled(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
lm.isLocationEnabled
} else {
val mode = Settings.Secure.getInt(
context.contentResolver, Settings.Secure.LOCATION_MODE,
Settings.Secure.LOCATION_MODE_OFF
)
mode != Settings.Secure.LOCATION_MODE_OFF
}
}
override fun onLocationAvailability(p0: LocationAvailability?) {
if (isLocationEnabled().not()) {
locationUpdateSubject.failed(LocationAvailabilityError)
}
}
as Google docs says:
When isLocationAvailable() returns false you can assume that location
will not be returned in onLocationResult(LocationResult) until
something changes in the device's settings or environment.
So you can assume that location may be not available not only because of disabled location settings, but because of signal strength, or maybe satellites are not visible or something else, it just indicates that you will not receive location updates until something changes. You can show user notification about it with something like "We can't get your location, try enable location settings"
I've faced same issue and same time I saw that location services has been enabled on the device and my ap had allowed permissions from user.
I still saw
com.google.android.gms.location.LocationCallback#onLocationAvailability LocationAvailability[isLocationAvailable: false]
The issue was that there is additional preference inside Location item on the phone. We have to choose Battery saving/High accuracy option. Please check out the screenshot provided:
I am developing an android application in which I need to get my current Location. I have successfully wrote the code and I am getting my current location using Google Play Service.
The problem is sometimes it gives me the location after a long time. I have noticed that it was only for first use of the app.
Any way to avoid this problem and get the current location fast? Is it related to the version of google play service in my code? (I am not using the last one in fact I am using version 9.8.0.)
As #tahsinRupam said, avoid using getLastLocation as it has a high tendency to return null. It also does not request a new location, so even if you get a location, it could be very old, and not reflect the current location. You might want to check the sample code in this thread: get the current location fast and once in android.
public void foo(Context context) {
// when you need location
// if inside activity context = this;
SingleShotLocationProvider.requestSingleUpdate(context,
new SingleShotLocationProvider.LocationCallback() {
#Override public void onNewLocationAvailable(GPSCoordinates location) {
Log.d("Location", "my location is " + location.toString());
}
});
}
You might want to verify the lat/long are actual values and not 0 or something. If I remember correctly this shouldn't throw an NPE but you might want to verify that.
Here's another SO post which might help:
What is the simplest and most robust way to get the user's current location on Android?
I can't seem to remove the listener for the update event for geolocation on Android.
I wanna stop the Geolocation on deactivate:
NativeApplication.nativeApplication.addEventListener(Event.DEACTIVATE, onAppDeactivate);
private function onAppDeactivate(e:Event):void {
if (Geolocation.isSupported) {
if (geolocation != null) {
geolocation.removeEventListener(GeolocationEvent.UPDATE, onGeolocationUpdate);
geolocation.setRequestedUpdateInterval(0);
geolocation = null;
}
}
}
I started with just removing the listener, but since that didn't work I also tried removing the geolocation all together. Still no luck..
Any hints?
I had an app that uses Geolocation too, and if what you want is to save the battery from draining fast, you have to call the geolocation.setRequestedUpdateInterval(INTERVAL_MILLIS) method with a high value for INTERVAL_MILLIS (in my case i use 60000 that is 1 min).
But as the (documentation) says, the OS will decide the update interval for the GPS, ant the value we pass serves a a "hint" to the update interval.
And specifically on Android, the GPS icon will stay on as long as you have the GPS enabled (and it will consume power) but will no drain you battery fast as the update interval will be high.
So the best thing you can do is actually request a large update interval when you app deactivates.
I'm trying to get user position in Sencha Touch 2:
var geo = new Ext.util.Geolocation({
autoUpdate: true,
allowHighAccuracy: true,
listeners: {
locationupdate: function(geo) {
lat = geo.getLatitude();
lon = geo.getLongitude();
// here comes processing the coordinates
}
}
});
But I've got coordinates only from network. There's no GPS icon on Android device, pointing that I'm using GPS. Also it doesn't work when I turn off internet connection. How can I enable GPS positioning? In Manifest file GPS is enabled.
I just got bit by this too.
It turns out there's a bug in Sencha: in Ext.util.Geolocation.parseOption they name the parameter allowHighAccuracy but the w3c specs name it enableHighAccuracy.
I added the following code to my application init to fix that:
var parseOptions = function() {
var timeout = this.getTimeout(),
ret = {
maximumAge: this.getMaximumAge(),
// Originally spells *allowHighAccurancy*
enableHighAccuracy: this.getAllowHighAccuracy()
};
//Google doesn't like Infinity
if (timeout !== Infinity) {
ret.timeout = timeout;
}
return ret;
};
Ext.util.Geolocation.override('parseOptions', parseOptions);
For the record: the bug has been reported over a year ago, and the fix was applied for TOUCH-2804 in a recent build. I don't know what that means, but sure enough the bug is still in 2.0
EDIT: using the approach mentioned above doesn't work well either. The GPS icon would turn on and off as Exts calls getCurrentPosition repeatedly using a setInterval. The reason for doing this is that The native watchPosition method is currently broken in iOS5. In my Android targeted application I ended up ditching Ext:util.Geolocation and directely used navigator.geolocation.watchPosition. After that it worked like a charm.
GPS location provider determines location using satellites. Depending on conditions, this provider may take a while to return a location fix.
Requires the permission android.permission.ACCESS_FINE_LOCATION.
Hey I think you need updated longitude and latitude values .
follow this link.. Click Here
I already tried it and test it in android mobile phone.. Its working.
Note : Must and should test in android mobile phone with on GPS status. It will not show on eclipse emulator. But it will show in mobile phone try it..
have a nice.
I want the exact location of user. So I created two listeners as:
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mGPSListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, mNetworkListener);
I am listening for location updates from both the network and gps providers and wanted to check the accurary of locations obtained by both so that I can pick the accurate location. I just want to ask am I using the right way of acheiving that...??? if not, please provide some guideline how to do this...???
My example code is:
if(MyGPSListener.currentLocation != null) {
if(MyNetworkListener.currentLocation != null) {
if(MyGPSListener.currentLocation.getAccuracy() <= MyNetworkListener.currentLocation.getAccuracy()) {
useGPSLocation();
}
else if(MyGPSListener.currentLocation.getAccuracy() > MyNetworkListener.currentLocation.getAccuracy()) {
useNetworkLocation();
}
}
else {
useGPSLocation();
}
}
else if(MyNetworkListener.currentLocation != null){
useNetworkLocation();
}
You should take a look at: http://developer.android.com/guide/topics/location/obtaining-user-location.html
Especially the function: isBetterLocation
I would probably use something like the isBetterLocation. Since it's actually quite important to see how old the location updates are. At least if you really want to know where the user is know and not where the user were.
But you can surely implement a time check in your own function which you described.
You can use https://github.com/balwinderSingh1989/androidBestLocationTracker
Android Best Location Tracker is an Android library that helps you get user best location with a object named BaseLocationStrategy that would give a accurate location using accuracy alogrithm.
It has added support till latest android api levels and can fetch location in background with ease.
For usage, refer "READ ME" of project.
You can check Google's offical document.
Define a model for the best performance
And Validate the accuracy of a location fix:
Check if the location retrieved is significantly newer than the previous estimate.
Check if the accuracy claimed by the location is better or worse than the previous estimate.
Check which provider the new location is from and determine if you trust it more.