我会阻止用户使用我的应用程序伪造该位置。
因此,我使用isFromMockProvider
检查位置是否为假(请按here)。但在某些情况下,isFromMockProvider()
可能会为伪造的地点返回false。
public void onLocationChanged(Location location) {
textView.append("long:"+location.getLatitude()+" - lat:"+location.getLongitude()+" - isMock :"+location.isFromMockProvider() + "\n");
}
我的情况:我使用app 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
答案 0 :(得分:11)
冒犯现实的答案
我想提供一个答案,帮助开发人员理解产品设计的公共关系方面,冒着批评的风险。坦率地说,人们无法在计算机科学真空中编写出色的应用程序。满足用户需求并平衡安全性是当今软件界面和行为设计的主要问题之一,尤其是在移动领域。
从这个角度来看,你的问题,"有没有办法检测用户在这种情况下是否使用假位置?"可能不是你面临的最相关的问题。我不会回答这个问题可能对你有所帮助,而且我可以很好地回答这个问题:"有没有办法安全地从用户的设备中获取数据&#39 ; s地理坐标固件,以至于它不能被欺骗?"
这个的答案是,"没有。"
Android HTTP客户合约
它不是Android客户端 - 服务器合同的一部分,也不是其竞争对手的合同,以保证用户设备位置信息。
实际原因
实际上市场力量可能会无限期地推动这种担保。许多设备所有者(以及您的用户)希望控制人们是否知道他们在隐私和家庭及家庭安全方面的真实位置。
<强>解决方案强>
您可以问自己作为软件设计师的下一个问题是,&#34;应用程序或库如何工作并满足我今天使用的特定百分比用户社区的需求#39 ; s(或明天的)位置欺骗软件?&#34;
如果您正在编写商业智能软件或您的系统还有其他一些统计方面,那么您需要相当于错误条的软件。如果显示统计数据,则误差线将是适当的图形功能。估计位置欺骗者在一群用户中的百分比需要进一步研究。
答案 1 :(得分:10)
我使用两种方法来识别假位置。
首先,我检查模拟位置,就像这里的其他代码一样。
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, false);
for (int i = runningApps.size() - 1; i >= 0; i--) {
String app = runningApps.get(i);
if(!hasAppPermission(context, app, "android.permission.ACCESS_MOCK_LOCATION")){
runningApps.remove(i);
}
}
return runningApps;
}
public static List<String> getRunningApps(Context context, boolean includeSystem) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<String> runningApps = new ArrayList<>();
List<ActivityManager.RunningAppProcessInfo> runAppsList = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runAppsList) {
for (String pkg : processInfo.pkgList) {
runningApps.add(pkg);
}
}
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();
}
List<ActivityManager.RunningServiceInfo> runningServices = activityManager.getRunningServices(1000);
for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) {
runningApps.add(serviceInfo.service.getPackageName());
}
runningApps = new ArrayList<>(new HashSet<>(runningApps));
if(!includeSystem){
for (int i = runningApps.size() - 1; i >= 0; i--) {
String app = runningApps.get(i);
if(isSystemPackage(context, app)){
runningApps.remove(i);
}
}
}
return 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;
}
答案 2 :(得分:4)
This SO question上的答案以及This SO question上的答案在较小程度上的答案似乎表明您在FusedLocationApi中遇到了一个不幸的缓存问题,该问题是由onLocationChanged以过时的时间戳调用的(因此忽略结果,因为它认为已有更新的数据。)
除非你没有改变......所以可以发现新的AP,我担心你只能获得缓存的位置。如果您想要新的位置,请使用GPS提供商。
解决方案是从GPS Provider调用位置,如此:
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new MyLocationListener();
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, locationListener);
(上面的代码来自较长的示例here)
答案 3 :(得分:1)
我在我的项目中使用这种方法,直到现在才能完美运行:
for api&lt; 18
//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&gt; = 18,您应该使用
location.isFromMockProvider();
关键是location.isFromMockProvider
是错误的,有时它会显示一个模拟位置,因为它可以!
此链接中有一个解决方法,其中包含完整的详细信息
Location on Android: Stop Mocking Me!
方法是:
请记住标记为模拟
如果新的“非模拟”阅读距离最后一个模拟1公里,则拒绝 它
仅在连续20次“非模拟”后清除最后一个模拟位置 读数。