不让APP在模拟器运行在特殊的需求下还是要做一个的。
首先你需要一个这样的工具类。
public class FindEmulator { // Need to check the format of these // Android emulator support up to 16 concurrent emulator // The console of the first emulator instance running on a given // machine uses console port 5554 // Subsequent instances use port numbers increasing by two private String[] known_numbers = { "15555215554", // Default emulator phone numbers + VirusTotal "15555215556", "15555215558", "15555215560", "15555215562", "15555215564", "15555215566", "15555215568", "15555215570", "15555215572", "15555215574", "15555215576", "15555215578", "15555215580", "15555215582", "15555215584",}; private String[] known_device_ids = {"000000000000000", // Default emulator id "e21833235b6eef10", // VirusTotal id "012345678912345"}; private String[] known_imsi_ids = {"310260000000000" // Default imsi id }; private String[] known_serial_number = {"89014103211118510720"}; private String[] known_pipes = {"/dev/socket/qemud", "/dev/qemu_pipe"}; private String[] known_files = {"/system/lib/libc_malloc_debug_qemu.so", "/sys/qemu_trace"}; // "/system/bin/qemu-props"}; SM-G3502I GT-I9308 有此文件 private String[] known_geny_files = {"/dev/socket/genyd", "/dev/socket/baseband_genyd"}; private String[] known_qemu_drivers = {"goldfish"}; /** * Known props, in the format of [property name, value to seek] if value to seek is null, then * it is assumed that the existence of this property (anything not null) indicates the QEmu * environment. */ private Property[] known_props = {new Property("init.svc.qemud", null), new Property("init.svc.qemu-props", null), new Property("qemu.hw.mainkeys", null), new Property("qemu.sf.fake_camera", null), new Property("qemu.sf.lcd_density", null), new Property("ro.bootloader", "unknown"), new Property("ro.bootmode", "unknown"), new Property("ro.hardware", "goldfish"), new Property("ro.kernel.android.qemud", null), new Property("ro.kernel.qemu.gles", null), new Property("ro.kernel.qemu", "1"), new Property("ro.product.device", "generic"), new Property("ro.product.model", "sdk"), new Property("ro.product.name", "sdk"), // Need to double check that an "empty" string ("") returns null new Property("ro.serialno", null)}; private TelephonyManager mTM = null; private final Context mContext; public FindEmulator(Context context) { mContext = context.getApplicationContext(); } /** * Check the existence of known pipes used by the Android QEmu environment. * * @return {@code true} if any pipes where found to exist or {@code false} if not. */ public boolean hasPipes() { for (String pipe : known_pipes) { File qemu_socket = new File(pipe); if (qemu_socket.exists()) { return true; } } return false; } public String getFirstExistsPipe() { for (String pipe : known_pipes) { File qemu_socket = new File(pipe); if (qemu_socket.exists()) { return pipe; } } return null; } /** * Check the existence of known files used by the Android QEmu environment. * * @return {@code true} if any files where found to exist or {@code false} if not. */ public boolean hasQEmuFiles() { for (String pipe : known_files) { File qemu_file = new File(pipe); if (qemu_file.exists()) { return true; } } return false; } public String getFirstExistsQEmuFile() { for (String file : known_files) { File geny_file = new File(file); if (geny_file.exists()) { return file; } } return null; } /** * Check the existence of known files used by the Genymotion environment. * * @return {@code true} if any files where found to exist or {@code false} if not. */ public boolean hasGenyFiles() { for (String file : known_geny_files) { File geny_file = new File(file); if (geny_file.exists()) { return true; } } return false; } public String getFirstExistsGenyFile() { for (String file : known_geny_files) { File geny_file = new File(file); if (geny_file.exists()) { return file; } } return null; } /** * Reads in the driver file, then checks a list for known QEmu drivers. * * @return {@code true} if any known drivers where found to exist or {@code false} if not. */ public boolean hasQEmuDrivers() { for (File drivers_file : new File[] {new File("/proc/tty/drivers"), new File("/proc/cpuinfo")}) { if (drivers_file.exists() && drivers_file.canRead()) { // We don't care to read much past things since info we care about should be // inside here byte[] data = new byte[1024]; try { InputStream is = new FileInputStream(drivers_file); is.read(data); is.close(); } catch (Exception exception) { exception.printStackTrace(); } String driver_data = new String(data); for (String known_qemu_driver : known_qemu_drivers) { if (driver_data.contains(known_qemu_driver)) { return true; } } } } return false; } public String getFristQEmuDriver() { for (File drivers_file : new File[] {new File("/proc/tty/drivers"), new File("/proc/cpuinfo")}) { if (drivers_file.exists() && drivers_file.canRead()) { // We don't care to read much past things since info we care about should be // inside here byte[] data = new byte[1024]; try { InputStream is = new FileInputStream(drivers_file); is.read(data); is.close(); } catch (Exception exception) { exception.printStackTrace(); } String driver_data = new String(data); for (String known_qemu_driver : known_qemu_drivers) { if (driver_data.contains(known_qemu_driver)) { return known_qemu_driver; } } } } return null; } public String getSerialNumber() { if (null == mTM) { mTM = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); } return mTM.getSimSerialNumber(); } public boolean hasKnownSerialNumber() { String phoneNumber = getSerialNumber(); for (String number : known_serial_number) { if (number.equalsIgnoreCase(phoneNumber)) { return true; } } return false; } public String getPhoneNumber() { if (null == mTM) { mTM = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); } return mTM.getLine1Number(); } public boolean hasKnownPhoneNumber() { String phoneNumber = getPhoneNumber(); for (String number : known_numbers) { if (number.equalsIgnoreCase(phoneNumber)) { return true; } } return false; } public String getDeviceId() { if (null == mTM) { mTM = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); } return mTM.getDeviceId(); } public boolean hasKnownDeviceId() { String deviceId = getDeviceId(); for (String known_deviceId : known_device_ids) { if (known_deviceId.equalsIgnoreCase(deviceId)) { return true; } } return false; } public String getImsi() { if (null == mTM) { mTM = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); } return mTM.getSubscriberId(); } public boolean hasKnownImsi() { String imsi = getImsi(); for (String known_imsi : known_imsi_ids) { if (known_imsi.equalsIgnoreCase(imsi)) { return true; } } return false; } public boolean hasEmulatorBuild() { // String BOARD = Build.BOARD; Coolpad 8298-A01 The name of the underlying board, like "unknown". // This appears to occur often on real hardware... that's sad // String BOOTLOADER = android.os.Build.BOOTLOADER; // The system bootloader version number. String BRAND = Build.BRAND; // The brand (e.g., carrier) the software is customized // for, if any. // "generic" String DEVICE = Build.DEVICE; // The name of the industrial design. "generic" String HARDWARE = Build.HARDWARE; // The name of the hardware (from the kernel command // line or // /proc). "goldfish" String MODEL = Build.MODEL; // The end-user-visible name for the end product. "sdk" String PRODUCT = Build.PRODUCT; // The name of the overall product. if ((BRAND.compareTo("generic") == 0) || (DEVICE.compareTo("generic") == 0) || (MODEL.compareTo("sdk") == 0) || (PRODUCT.compareTo("sdk") == 0) || (HARDWARE.compareTo("goldfish") == 0)) { return true; } return false; } public String getOperatorName() { if (null == mTM) { mTM = ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)); } return mTM.getNetworkOperatorName(); } public boolean isOperatorNameAndroid() { String szOperatorName = getOperatorName(); return szOperatorName.equalsIgnoreCase("android"); } /** * Will query specific system properties to try and fingerprint a QEmu environment. A minimum * threshold must be met in order to prevent false positives. * * @return {@code true} if enough properties where found to exist or {@code false} if not. */ public boolean hasQEmuProps() { int found_props = 0; for (Property property : known_props) { String property_value = getProp(mContext, property.name); // See if we expected just a non-null if (TextUtils.isEmpty(property.seek_value) && !TextUtils.isEmpty(property_value)) { found_props++; } else if (!TextUtils.isEmpty(property.seek_value) && !TextUtils.isEmpty(property_value) && (property_value.contains(property.seek_value))) { // See if we expected a value to seek found_props++; } } /* The "known" props have the potential for false-positiving due to interesting (see: poorly) made Chinese devices/odd ROMs. Keeping this threshold low will result in better QEmu detection with possible side affects. */ if (found_props >= 5) { return true; } return false; } public String getQEmuProps() { String ret = ""; for (Property property : known_props) { String property_value = getProp(mContext, property.name); // See if we expected just a non-null if (TextUtils.isEmpty(property.seek_value) && !TextUtils.isEmpty(property_value)) { ret += property.name + ":" + property_value + "\n"; } else if (!TextUtils.isEmpty(property.seek_value) && !TextUtils.isEmpty(property_value) && (property_value.contains(property.seek_value))) { // See if we expected a value to seek ret += property.name + ":" + property_value + "\n"; } } return ret; } /** * Method to reflectively invoke the SystemProperties.get command - which is the equivalent to * the adb shell getProp command. * * @param context * A {@link Context} object used to get the proper ClassLoader (just needs to be * Application Context object) * @param property * A {@code String} object for the property to retrieve. * * @return {@code String} value of the property requested. */ public String getProp(Context context, String property) { try { ClassLoader classLoader = context.getClassLoader(); Class<?> systemProperties = classLoader.loadClass("android.os.SystemProperties"); Method get = systemProperties.getMethod("get", String.class); Object[] params = new Object[1]; params[0] = new String(property); return (String) get.invoke(systemProperties, params); } catch (IllegalArgumentException iAE) { throw iAE; } catch (Exception exception) { throw null; } } public static class Property { public String name; public String seek_value; public Property(String name, String seek_value) { this.name = name; this.seek_value = seek_value; } }}
接下来你需要在你的主启动Activity里写这么一个方法:
private void isEmulator(){ FindEmulator emulator = new FindEmulator(this); boolean isEmulator = false; String detailStr = ""; if (emulator.hasEmulatorBuild()) { isEmulator = true; detailStr = "Brand: " + Build.BRAND + "\n" + "Device: " + Build.DEVICE + "\n" + "Hardware: " + Build.HARDWARE + "\n" + "Product: " + Build.PRODUCT + "\n" + "Serial: " + Build.SERIAL + "\n" + "Model: " + Build.MODEL + "\n"; } else if (emulator.hasKnownDeviceId()) { isEmulator = true; detailStr = "getDeviceId: " + emulator.getDeviceId(); } else if (emulator.hasKnownImsi()) { isEmulator = true; detailStr = "getImsi: " + emulator.getImsi(); } else if (emulator.hasKnownPhoneNumber()) { isEmulator = true; detailStr = "getPhoneNumber: " + emulator.getPhoneNumber(); } else if (emulator.hasKnownSerialNumber()) { isEmulator = true; detailStr = "getSerialNumber: " + emulator.getSerialNumber(); } else if (emulator.hasPipes()) { isEmulator = true; detailStr = "hasPipes: " + emulator.getFirstExistsPipe(); } else if (emulator.hasGenyFiles()) { isEmulator = true; detailStr = "hasGenyFiles: " + emulator.getFirstExistsGenyFile(); } else if (emulator.hasQEmuDrivers()) { isEmulator = true; detailStr = "hasQEmuDrivers: " + emulator.getFristQEmuDriver(); } else if (emulator.hasQEmuProps()) { isEmulator = true; detailStr = "hasQEmuProps: " + emulator.getQEmuProps(); } else if (emulator.isOperatorNameAndroid()) { isEmulator = true; detailStr = "operatorNameAndroid: " + emulator.getOperatorName(); } if (isEmulator) { mHandler.obtainMessage(MSG_IS_EMULATOR, "模拟器安装退出异常:"+detailStr).sendToTarget(); } }