Android模擬器檢測方法有哪些?
作者:Reathin
隨著Android系統的更新,模擬器的增多,需要具體研究對應的一些變動來更新上述代碼。最終判定結果不一定能檢測出所有的模擬器,但是一定不能誤殺真機影響用戶正常使用。
Android模擬器的檢測,一般方法是通過檢查設備的一些特征來判斷應用是否運行在模擬器。
- 檢查Build信息:通過讀取設備的Build信息來判斷是否在模擬器上運行。例如,檢查設備的Build.MODEL和Build.MANUFACTURER是否包含常見的模擬器關鍵詞,如"generic"、"sdk"等。
- 檢查硬件特征:模擬器通常會模擬一些硬件特征,如IMEI、MAC地址等。通過檢查這些硬件特征來判斷是否在模擬器上運行。
- 檢查虛擬化指令集:模擬器通常會使用虛擬化指令集來模擬硬件,通過檢查CPU的指令集來判斷是否在模擬器上運行。
- 檢查運行環境:通過檢查設備的運行環境,如是否有電話功能、GPS功能等來判斷是否在模擬器上運行。
這些方法都不是絕對可靠的,模擬器的不斷發展可能會繞過這些檢測方法。在實際應用中,通過綜合多種方法進行檢測,以提高準確性。
普遍檢測方法
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;
}
以上方法檢測模擬器有兩個問題:
- 撥號檢測,Android10.0及以上均為false,Android10.0以上會誤判。
- Build.SERIAL,Android8.0以上均為unknown導致8.0以上系統均會被誤判。
推薦檢測方法
設備信息檢測
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檢測,配合上面的固件信息,基本可以搞定大部分模擬器。
藍牙檢測
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;
}
}
}
光線傳感器檢測
public static Boolean notHasLightSensorManager(Context context) {
SensorManager sensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
//光
if (null == sensor) {
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系統的更新,模擬器的增多,需要具體研究對應的一些變動來更新上述代碼。最終判定結果不一定能檢測出所有的模擬器,但是一定不能誤殺真機影響用戶正常使用。
責任編輯:武曉燕
來源:
沐雨花飛蝶