Android API 31 以下模拟位置检测最佳实践
2025-01-25 21:08:06
Android API 31 以下模拟位置检测方案
Android应用中检测模拟位置对安全性和数据准确性至关重要。Location.isFromMockProvider()
方法虽已废弃,但在API级别低于31的设备上,直接使用会导致问题,它始终返回 false
,即使正在使用模拟位置。这就需要针对不同API级别设备,找到合适的模拟位置检测方案。
方案一:检查允许模拟位置的应用
一种方案是检查是否开启了开发者选项中的允许模拟位置功能,并识别提供模拟位置的应用。 这是一种在较低API级别设备上较可靠的方式。具体做法如下:
-
获取应用列表: 获取系统中已安装应用的列表。
-
检查
Settings.Secure.ALLOW_MOCK_LOCATION
: 读取这个系统设置,判断模拟位置是否已开启。 -
检查Provider: 获取模拟位置应用所在的
PackageName
, 利用系统API的特性, 检测特定的位置提供程序, 然后在特定条件和特定Android版本检测,返回是否被 Mock, 该部分实现见代码实例部分。如果允许模拟位置并且已启用模拟位置提供程序,则可以相当确信位置是模拟的。但是请注意, 如果设备开启允许模拟位置的应用, 返回的结果会是真实位置, 而不是被Mock的位置.
-
需要注意
- 该方案依赖于
Settings.Secure.ALLOW_MOCK_LOCATION
的设置状态和已安装的应用情况,有一定的局限性。 - 一些厂商的定制系统可能对此项设置有影响,请充分测试不同设备下的兼容性。
- 该方案依赖于
-
附加的安全提示
- 请谨慎处理从位置API收集的任何信息。 用户可能会欺骗该API,但不能欺骗该信息对于您的应用的意义。请务必彻底验证位置。
- 无论您的位置数据来源如何,都请进行验证,而不要仅仅信任它是正确的。
代码示例:
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.provider.Settings;
import java.util.List;
public class MockLocationDetector {
private final Context context;
public MockLocationDetector(Context context) {
this.context = context;
}
public boolean isMockLocationEnabled(){
// Android 23及更高版本使用Secure
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION,0) != 0;
} else {
// 在低于API 23版本中使用Setting
return Settings.System.getInt(context.getContentResolver(),Settings.Secure.ALLOW_MOCK_LOCATION,0) !=0;
}
}
public boolean isMockLocation(Location location) {
//如果SDK_INT >=31, 使用 isMock()
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
return location.isMock();
}
// 如果SDK_INT <31, 需要检查是否是mock providers
boolean isMockSettingsON = isMockLocationEnabled();
if(!isMockSettingsON) {
return false;
}
String mockProviders = android.provider.Settings.Secure.getString(
context.getContentResolver(), android.provider.Settings.Secure.MOCK_LOCATION);
if(mockProviders==null|| "".equals(mockProviders)) {
return false;
}
List<String> providerlist=java.util.Arrays.asList(mockProviders.split(","));
return providerlist.contains(location.getProvider());
}
}
步骤:
- 创建一个
MockLocationDetector
类,构造方法传入Context
。 - 实现
isMockLocationEnabled()
, 使用Settings 获取ALLOW_MOCK_LOCATION 的值。 - 实现
isMockLocation(Location location)
,根据API级别选择对应的mock 监测方案。- API 31+直接使用
location.isMock()
。 - API 31以下先检查
Settings.Secure.ALLOW_MOCK_LOCATION
是否允许Mock, 再检测获取模拟位置 provider是否包含在系统的 Provider列表里。
- API 31+直接使用
- 在代码中使用:
boolean isMocked = new MockLocationDetector(context).isMockLocation(location);
- 可以对返回的boolean 进行判断,如果
isMocked == true
代表为Mock,反之则为真。
- 可以对返回的boolean 进行判断,如果
方案二: 使用其他位置数据
由于Android系统可能修改 Settings.Secure.ALLOW_MOCK_LOCATION
或存在设备差异,完全依赖该值可能无法百分百准确地判断模拟位置。另外一种增强可靠性的方法,同时利用系统其它传感器信息,并比对他们的差异,实现模拟位置的综合检测。
- 传感器信息
- 加速度计和陀螺仪, 获取传感器的原始数据,分析其数据分布。如果位置移动与传感器的活动不符,有可能表明是模拟位置。
-
网络位置和基站信息
将位置数据和网络信息和周围基站信息比较, 模拟位置无法轻易模拟附近的手机信号基站信息。将定位与网络或者基站信息进行对比,能够判断当前的位置的真实性。
- 获取WIFI或者基站信息,比对网络环境位置。模拟位置很难模拟手机附近的WIFI以及手机基站位置信息。
- 获取位置附近区域是否有地标建筑,比如: 商场, 学校,医院, 等信息。根据比对结果,可以一定程度确认是否是 Mock。
- 实现注意点:
- 该方案的复杂性更高,需要较多的计算和数据分析。实现过程和方案比较复杂,本文档不赘述详细的代码示例。
- 实现该方案需要综合考量设备差异,资源消耗等因素。建议按需取用,选择合适你的应用场景。
步骤:
- 集成Android传感器服务,获取加速度和陀螺仪的数据,分析移动特征。
- 集成位置信息服务,获取网络定位,wifi基站信息,并进行比对分析。
- 综合分析多方面的信息数据,判断当前位置是否可信。
检测模拟位置需要一种综合的方法。单独使用Location.isMock()
在低版本上不适用,依赖单个方案会有局限性。组合多种方式检测,能更好的避免各种模拟位置的绕过和干扰,有效提高数据安全性。 实际项目中,请务必全面测试各种Android版本,保证应用的鲁棒性。