Android模擬器最新檢測方法詳解
最近看到某客戶端有一個檢測模擬器的方法,我正常手機(jī)結(jié)果被判斷是模擬器了,很好奇,于是找了一下原因。
普遍檢測方法
public boolean isEmulator() { String url = "tel:" + "123456"; Intent intent = new Intent(); intent.setData(Uri.parse(url)); intent.setAction(Intent.ACTION_DIAL); // 是否可以處理跳轉(zhuǎn)到撥號的 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
這導(dǎo)致8.0以上系統(tǒng)均會被誤判
推薦模擬器檢測方法
設(shè)備信息檢測
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; }
上述代碼使用了多種方法來檢測設(shè)備是否為模擬器,這些方法包括:
- 檢測 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”
- 檢測設(shè)備的電話號碼是否為已知的模擬器電話號碼
上述方法都是基于固件信息的判斷,通過測試發(fā)現(xiàn)很多模擬器都失效,參考網(wǎng)上的教程,還有藍(lán)牙、光線傳感器、cpu檢測,配合上面的固件信息,基本可以搞定大部分模擬器。
藍(lán)牙檢測方法
public boolean notHasBlueTooth() { BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter(); if (ba == null) { return true; } else { // 如果有藍(lán)牙不一定是有效的。獲取藍(lán)牙名稱,若為null 則默認(rèn)為模擬器 String name = ba.getName(); if (TextUtils.isEmpty(name)) { return true; } else { return false; } } }
光傳感器檢測方法
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檢測方法
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系統(tǒng)的更新,模擬器的增多,我們需要具體研究對應(yīng)的一些變動來更新上述代碼。
我們檢測要注意一個問題,不一定能檢測出所有的模擬器,但是一定不能誤殺真機(jī)。
總結(jié)
建議 首先使用傳感器進(jìn)行可疑性判斷
藍(lán)牙, wifi, 電池 可以作為 輔助數(shù)據(jù)進(jìn)行監(jiān)聽。
附加傳感器類型代碼
public String getSensorTypeName(int type) { switch (type) { case Sensor.TYPE_ACCELEROMETER: return "加速度傳感器"; case Sensor.TYPE_GYROSCOPE: return "陀螺儀傳感器"; case Sensor.TYPE_LIGHT: return "環(huán)境光線傳感器"; case Sensor.TYPE_MAGNETIC_FIELD: return "電磁場傳感器"; case Sensor.TYPE_ORIENTATION: return "方向傳感器"; case Sensor.TYPE_PRESSURE: return "壓力傳感器"; case Sensor.TYPE_PROXIMITY: return "距離傳感器"; case Sensor.TYPE_TEMPERATURE: return "溫度傳感器"; case Sensor.TYPE_GRAVITY: return "重場傳感器"; case Sensor.TYPE_LINEAR_ACCELERATION: return "線性加速度傳感器"; case Sensor.TYPE_ROTATION_VECTOR: return "旋轉(zhuǎn)矢量傳感器"; case Sensor.TYPE_RELATIVE_HUMIDITY: return "濕度傳感器"; case Sensor.TYPE_AMBIENT_TEMPERATURE: return "溫度傳感器"; case Sensor.TYPE_GAME_ROTATION_VECTOR: return "游戲旋轉(zhuǎn)矢量傳感器"; case Sensor.TYPE_STEP_COUNTER: return "計步器"; case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR: return "地磁旋轉(zhuǎn)矢量傳感器"; case Sensor.TYPE_SIGNIFICANT_MOTION: return "特殊動作觸發(fā)傳感器"; default: return "未知傳感器"; } }
到此這篇關(guān)于Android模擬器最新檢測方法詳解的文章就介紹到這了,更多相關(guān)Android模擬器檢測內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android仿美團(tuán)下拉菜單(商品選購)實(shí)例代碼
這篇文章主要介紹了Android仿美團(tuán)下拉菜單(商品選購)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-03-03Android使用kotlin實(shí)現(xiàn)多行文本上下滾動播放
這篇文章主要為大家詳細(xì)介紹了Android使用kotlin實(shí)現(xiàn)多行文本的上下滾動播放,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01Android實(shí)現(xiàn)桌面懸浮窗、蒙板效果實(shí)例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)桌面懸浮窗、蒙板效果實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-05-05Android中使用Handler及Countdowntimer實(shí)現(xiàn)包含倒計時的閃屏頁面
這篇文章主要介紹了Android中使用Handler及Countdowntimer實(shí)現(xiàn)包含倒計時的閃屏頁面,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-03-03Android View事件分發(fā)和消費(fèi)源碼簡單理解
這篇文章主要介紹了Android View事件分發(fā)和消費(fèi)源碼簡單理解的相關(guān)資料,需要的朋友可以參考下2017-07-07Android手機(jī)信號強(qiáng)度檢測詳細(xì)介紹
這篇文章主要介紹了Android手機(jī)信號強(qiáng)度檢測的相關(guān)資料,android定義了2種信號單位:dBm和asu。具體兩種的關(guān)系本文給大家介紹非常詳細(xì),需要的朋友可以參考下2016-11-11Android中實(shí)現(xiàn)HashMap排序的方法
這篇文章主要介紹了Android中實(shí)現(xiàn)HashMap排序的方法,很經(jīng)典的一種排序算法,需要的朋友可以參考下2014-08-08