检测或阻止用户是不是使用虚假位置
Posted
技术标签:
【中文标题】检测或阻止用户是不是使用虚假位置【英文标题】:Detect or prevent if user uses fake location检测或阻止用户是否使用虚假位置 【发布时间】:2017-07-22 23:52:37 【问题描述】:如果用户伪造位置,我将阻止他们使用我的应用。
所以我使用isFromMockProvider
来检查位置是否是假的(关注here)。但在某些情况下,isFromMockProvider()
可能会为伪造的位置返回 false。
public void onLocationChanged(Location location)
textView.append("long:"+location.getLatitude()+" - lat:"+location.getLongitude()+" - isMock :"+location.isFromMockProvider() + "\n");
我的情况是:我使用应用程序Fake GPS location 伪造一个位置,然后我禁用虚假位置并转到我的应用程序。然后 onLocationChanged 返回带有isFromMockProvider() = false
的假位置@
录像机:https://www.youtube.com/watch?v=rWVvjOCaZiI(在这个视频中,我的当前位置是 16.06, 108.21,假的位置是 36.26,138.28。你可以在上一个视频中看到位置是 36.26,138.28 但是isFromMockProvider=false)
在这种情况下,有什么方法可以检测用户是否使用了虚假位置?任何帮助或建议将不胜感激。DEMO project
【问题讨论】:
当然,如果您禁用虚假位置,那么 getLastLocation 会返回真实位置,并且“isFromMockProvider() = false”是正确的结果... @NickCardoso 绝对不,它不会返回真实位置,getLastLocation
将返回虚假位置。我已经检查过很多次了:(
@NickCardoso 你错了,他已经指出“getLastLocation 返回假位置”,而不是真实位置
那些不是真的假的假位置返回的时间戳是什么?
此问题是常见问题。我能问一下你奖励赏金给一个没有解决你问题的“答案”的动机吗?
【参考方案1】:
冒着现实答案的风险
我想提供一个答案,帮助开发人员了解产品设计的公共关系方面,冒着被批评的风险。坦率地说,一个人无法在计算机科学的真空中编写出色的应用程序。满足用户需求并将其与安全性平衡是当今软件界面和行为设计的主要问题之一,尤其是在移动领域。
从这个角度来看,您的问题是“在这种情况下,有没有办法检测用户是否使用了虚假位置?”可能不是您面临的最相关的问题。我问这个可能对您有更多帮助的问题并没有回避,我可以很好地回答:“有什么方法可以安全地从用户设备的地理坐标固件中获取数据,这样就不会被欺骗?”
这个问题的答案是“不”。
Android HTTP 客户端合约
保证用户设备位置信息不是 android 客户端-服务器合同或其竞争对手的合同的一部分。
实际原因
实际上有一种市场力量可能会无限期地反对这种保证。许多设备所有者(和您的用户)出于隐私以及家庭和家庭安全的原因,希望控制人们是否知道他们的真实位置。
解决方案
作为软件设计师,您可以问自己的下一个问题是:“应用程序或库如何工作并满足我寻求满足使用今天(或明天)位置的一定比例的用户社区的需求欺骗软件?”
如果您正在编写商业智能软件或您的系统还有其他一些统计方面,那么您需要软件相当于误差线。如果您显示统计数据,那么误差线将是一个合适的图形功能。估计用户群中位置欺骗者的百分比需要进一步研究。
【讨论】:
最好的方法是真正收集数据并使用 ML 找出哪些用户在欺骗。【参考方案2】:我使用两种方法来识别虚假位置。 首先,我检查模拟位置,就像这里的其他代码一样。
public static boolean isMockLocationOn(Location location, Context context)
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
return location.isFromMockProvider();
else
String mockLocation = "0";
try
mockLocation = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION);
catch (Exception e)
e.printStackTrace();
return !mockLocation.equals("0");
其次,我检查正在运行的应用程序和服务,它们需要访问模拟位置的权限。
public static List<String> getListOfFakeLocationApps(Context context)
List<String> runningApps = getRunningApps(context);
List<String> fakeApps = new ArrayList<>();
for (String app : runningApps)
if(!isSystemPackage(context, app) && hasAppPermission(context, app, "android.permission.ACCESS_MOCK_LOCATION"))
fakeApps.add(getApplicationName(context, app));
return fakeApps;
public static List<String> getRunningApps(Context context, boolean includeSystem)
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
HashSet<String> runningApps = new HashSet<>();
try
List<ActivityManager.RunningAppProcessInfo> runAppsList = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runAppsList)
runningApps.addAll(Arrays.asList(processInfo.pkgList));
catch (Exception ex)
ex.printStackTrace();
try
//can throw securityException at api<18 (maybe need "android.permission.GET_TASKS")
List<ActivityManager.RunningTaskInfo> runningTasks = activityManager.getRunningTasks(1000);
for (ActivityManager.RunningTaskInfo taskInfo : runningTasks)
runningApps.add(taskInfo.topActivity.getPackageName());
catch (Exception ex)
ex.printStackTrace();
try
List<ActivityManager.RunningServiceInfo> runningServices = activityManager.getRunningServices(1000);
for (ActivityManager.RunningServiceInfo serviceInfo : runningServices)
runningApps.add(serviceInfo.service.getPackageName());
catch (Exception ex)
ex.printStackTrace();
return new ArrayList<>(runningApps);
public static boolean isSystemPackage(Context context, String app)
PackageManager packageManager = context.getPackageManager();
try
PackageInfo pkgInfo = packageManager.getPackageInfo(app, 0);
return (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
catch (PackageManager.NameNotFoundException e)
e.printStackTrace();
return false;
public static boolean hasAppPermission(Context context, String app, String permission)
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try
packageInfo = packageManager.getPackageInfo(app, PackageManager.GET_PERMISSIONS);
if(packageInfo.requestedPermissions!= null)
for (String requestedPermission : packageInfo.requestedPermissions)
if (requestedPermission.equals(permission))
return true;
catch (PackageManager.NameNotFoundException e)
e.printStackTrace();
return false;
public static String getApplicationName(Context context, String packageName)
String appName = packageName;
PackageManager packageManager = context.getPackageManager();
try
appName = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)).toString();
catch (PackageManager.NameNotFoundException e)
e.printStackTrace();
return appName;
(更新)
不幸的是,谷歌禁止应用程序接收当前正在运行的应用程序列表。 (从 5.1.1 开始,但我仍然可以在运行 android 7.1 的测试设备中获取应用列表)
现在您可以使用 UsageStatsManager 仅获取最近使用的应用程序列表(具有请求运行时权限),例如这里Android 5.1.1 and above - getRunningAppProcesses() returns my application package only
所以如果用户关闭或退出虚假定位应用程序,我无法确定。
现在,要获取虚假位置应用程序列表,我尝试获取位置,如果 location.isFromMockProvider() 返回 true,我会扫描设备以查找所有已安装的应用程序,这些应用程序需要访问模拟位置的权限,如下所示:
public static List<String> getListOfFakeLocationAppsFromAll(Context context)
List<String> fakeApps = new ArrayList<>();
List<ApplicationInfo> packages = context.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo aPackage : packages)
boolean isSystemPackage = ((aPackage.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
if(!isSystemPackage && hasAppPermission(context, aPackage.packageName, "android.permission.ACCESS_MOCK_LOCATION"))
fakeApps.add(getApplicationName(context, aPackage.packageName));
return fakeApps;
【讨论】:
在我的情况下,'getListOfFakeLocationApps' 总是返回空列表。 @Makalele 请尝试调试代码,看看为什么它是空的。您真的要替换非 root 设备上的位置吗?你能帮我改进我的代码吗? getRunningApps 只返回我的应用程序,我没有根设备。没有抛出异常。【参考方案3】:This SO question 上的答案以及在较小程度上This SO question 上的答案似乎表明您在 FusedLocationApi 中遇到了不幸的缓存问题,这是由于 onLocationChanged 被调用时使用过时的时间戳(因此忽略结果为它认为已经有更新的数据)。
引用Reno's answer:
除非您没有更改...以便可以发现新的 AP,否则您将只能获得缓存的位置。如果您想要新的位置,请使用 GPS 提供商。
解决方案将改为从GPS Provider 调用位置,如下所示:
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new MyLocationListener();
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, locationListener);
(上面的代码来自一个更长的例子here)
【讨论】:
【参考方案4】:我在我的项目中使用了这种方法,并且到目前为止它工作得很好:
对于 api
//returns true if mock location enabled, false if not enabled.
public static boolean isMockLocationOn(Context context)
if (Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
对于 api >= 18 你应该使用
location.isFromMockProvider();
重点是location.isFromMockProvider
有问题,有时它会显示一个模拟位置,因为它可以!
此链接中有一个解决方法,其中包含完整的详细信息
Location on Android: Stop Mocking Me!
方法是:
记住最近标记为模拟的位置
如果新的“非模拟”读数在上一次模拟的 1 公里范围内,则拒绝 它。
仅在连续 20 次“非模拟”后清除最后一个模拟位置 读数。
【讨论】:
在 android > 6 中,此方法将不再有效。这意味着你的方法总是返回 false。我敢肯定,你可以测试它 谢谢,但这里有一些遗漏。请再次检查我的问题。现在我正在使用isFromMockProvider
,isFromMockProvider
有一些问题,所以我问了这个问题。其次,您提供的链接在这种情况下无法正常工作:用户通过 FakeGPS 应用程序伪造位置,然后用户在转到我的应用程序之前禁用虚假位置。此时,most recent location labeled as a mock
不存在且isFromMockProvider
= false以上是关于检测或阻止用户是不是使用虚假位置的主要内容,如果未能解决你的问题,请参考以下文章