Android 模拟器检测

文章目录

最近看到某客户端有一个检测模拟器的方法,我正常手机结果被判断是模拟器了,很好奇,于是找了一下原因。

普遍检测代码如下:

java 复制代码
public boolean isEmulator() {
    String url = "tel:" + "123456";
    Intent intent = new Intent();
    intent.setData(Uri.parse(url));
    intent.setAction(Intent.ACTION_DIAL);
    // 是否可以处理跳转到拨号的 Intent
    boolean canResolveIntent = intent.resolveActivity(mContext.getPackageManager()) != null;

    return Build.FINGERPRINT.startsWith("generic")
        || Build.FINGERPRINT.toLowerCase().contains("vbox")
        || Build.FINGERPRINT.toLowerCase().contains("test-keys")
        || Build.MODEL.contains("google_sdk")
        || Build.MODEL.contains("Emulator")
        || Build.SERIAL.equalsIgnoreCase("unknown")
        || Build.SERIAL.equalsIgnoreCase("android")
        || Build.MODEL.contains("Android SDK built for x86")
        || Build.MANUFACTURER.contains("Genymotion")
        || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
        || "google_sdk".equals(Build.PRODUCT)
        || ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
            .getNetworkOperatorName().toLowerCase().equals("android")
        || !canResolverIntent;
}

这个代码检测模拟器有两个问题:

1、拨号检测,Android10.0及以上均为false

2、Build.SERIAL,Android8.0以上均为unknown

这导致8.0以上系统均会被误判

推荐模拟器检测方法:

设备信息检测代码:
java 复制代码
private static final String[] known_numbers = {"15555215554", "15555215556", "15555215558", "15555215560", "15555215562", "15555215564", "15555215566", "15555215568", "15555215570", "15555215572", "15555215574", "15555215576", "15555215578", "15555215580", "15555215582", "15555215584",};

private boolean detectEmulator() {
        if (Build.FINGERPRINT.startsWith("generic") || Build.FINGERPRINT.startsWith("unknown")
                || Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86") || Build.MANUFACTURER.contains("Genymotion")
                || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk".equals(Build.PRODUCT)) {
            return true;
        }
        if (Build.PRODUCT.equals("sdk") || Build.PRODUCT.equals("sdk_x86")
                || Build.PRODUCT.equals("vbox86p") || Build.PRODUCT.equals("emulator")) {
            return true;
        }
        if (Build.BOARD == null) {
            return true;
        }
        if (Build.BOARD.equals("unknown")
                || Build.BOARD.contains("android")
                || Build.BOARD.contains("droid")) {
            return true;
        }
        if (Build.DEVICE == null) {
            return true;
        }
        if (Build.DEVICE.equals("unknown")
                || Build.DEVICE.contains("android")
                || Build.DEVICE.contains("droid")) {
            return true;
        }
        if (Build.HARDWARE == null) {
            return true;
        }
        if (Build.HARDWARE.equals("goldfish")
                || Build.HARDWARE.equals("ranchu")
                || Build.HARDWARE.contains("ranchu")) {
            return true;
        }
        if (Build.BRAND == null) {
            return true;
        }
        if (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) {
            return true;
        }
        if (Build.MANUFACTURER.equals("unknown")) {
            return true;
        }
        if (Build.MANUFACTURER.equals("Genymotion")) {
            return true;
        }
        if ((Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk".equals(Build.PRODUCT)) {
            return true;
        }
        if (Build.PRODUCT == null) {
            return true;
        }
        if (Build.PRODUCT.equals("sdk")
                || Build.PRODUCT.equals("sdk_x86")
                || Build.PRODUCT.equals("vbox86p")
                || Build.PRODUCT.equals("emulator")) {
            return true;
        }
        if (Build.HARDWARE.equals("goldfish")
                || Build.HARDWARE.equals("ranchu")) {
            return true;
        }
        if (Build.FINGERPRINT.startsWith("generic")
                || Build.FINGERPRINT.startsWith("unknown")
                || Build.MODEL.contains("google_sdk")
                || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86")
                || Build.MANUFACTURER.contains("Genymotion")
                || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk".equals(Build.PRODUCT)) {
            return true;
        }
        if (Build.PRODUCT == null) {
            return true;
        }
        if (Build.PRODUCT.equals("sdk")
                || Build.PRODUCT.equals("sdk_x86")
                || Build.PRODUCT.equals("vbox86p")
                || Build.PRODUCT.equals("emulator")) {
            return true;
        }
        if (Build.HARDWARE.equals("goldfish")
                || Build.HARDWARE.equals("ranchu")) {
            return true;
        }
        if (new File("/dev/socket/qemud").exists()
                || new File("/dev/qemu_pipe").exists()) {
            return true;
        }
        try {
            TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
            if (telephonyManager != null) {
                String deviceId = telephonyManager.getDeviceId();
                List<String> knownNumbers = Arrays.asList(known_numbers);
                if (knownNumbers.contains(deviceId)) {
                    return true;
                }
            }
        } catch (Exception e) {
        }
        return false;
    }

上述代码使用了多种方法来检测设备是否为模拟器,这些方法包括:

  • 检测 Build.FINGERPRINT 是否以 "generic" 或 "unknown" 开头
  • 检测 Build.MODEL 是否包含 "google_sdk"、"Emulator" 或 "Android SDK built for x86"
  • 检测 Build.MANUFACTURER 是否为 "Genymotion"
  • 检测 Build.PRODUCT 是否为 "sdk"、"sdk_x86"、"vbox86p" 或 "emulator"
  • 检测 Build.BOARD 是否为 "unknown" 或包含 "android" 或 "droid"
  • 检测 Build.DEVICE 是否为 "unknown" 或包含 "android" 或 "droid"
  • 检测 Build.HARDWARE 是否为 "goldfish"、"ranchu" 或包含 "ranchu"
  • 检测 Build.BRAND 是否以 "generic" 开头,且 Build.DEVICE 以 "generic" 开头
  • 检测 Build.PRODUCT 是否为 "google_sdk"
  • 检测是否存在文件 "/dev/socket/qemud" 或 "/dev/qemu_pipe"
  • 检测设备的电话号码是否为已知的模拟器电话号码

上述方法都是基于固件信息的判断,通过测试发现很多模拟器都失效,参考网上的教程,还有蓝牙、光线传感器、cpu检测,配合上面的固件信息,基本可以搞定大部分模拟器。

蓝牙检测代码:
java 复制代码
public boolean notHasBlueTooth() {
    BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
    if (ba == null) {
        return true;
    } else {
        // 如果有蓝牙不一定是有效的。获取蓝牙名称,若为null 则默认为模拟器
        String name = ba.getName();
        if (TextUtils.isEmpty(name)) {
            return true;
        } else {
            return false;
        }
    }
}
光传感器检测代码:
java 复制代码
public static Boolean notHasLightSensorManager(Context context) {
    SensorManager sensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
    Sensor sensor8 = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); //光
    if (null == sensor8) {
        return true;
    } else {
        return false;
    }
}
CPU检测代码:
java 复制代码
public static boolean checkIsNotRealPhone() {
    String cpuInfo = readCpuInfo();
    if ((cpuInfo.contains("intel") || cpuInfo.contains("amd"))) {
        return true;
    }
    return false;
}

public static String readCpuInfo() {
    String result = "";
    try {
        String[] args = {"/system/bin/cat", "/proc/cpuinfo"};
        ProcessBuilder cmd = new ProcessBuilder(args);

        Process process = cmd.start();
        StringBuffer sb = new StringBuffer();
        String readLine = "";
        BufferedReader responseReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
        while ((readLine = responseReader.readLine()) != null) {
            sb.append(readLine);
        }
        responseReader.close();
        result = sb.toString().toLowerCase();
    } catch (IOException ex) {
    }
    return result;
}

以上检测方法也不是完全可行,随着Android系统的更新,模拟器的增多,我们需要具体研究对应的一些变动来更新上述代码。

我们检测要注意一个问题,不一定能检测出所有的模拟器,但是一定不能误杀真机。

相关推荐
还鮟1 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡3 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi003 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil5 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你5 小时前
Android View的绘制原理详解
android
移动开发者1号8 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号8 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best13 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk13 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭17 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin