当前位置:   article > 正文

安卓判断是否是模拟器,适配主流雷电,MUMU,夜神,逍遥_游戏检测安卓模拟器

游戏检测安卓模拟器

前言

最近游戏项目组又有新的要求,对于数据上报和数据统计接口,尽可能的具体化,比如是否是模拟器,模拟器的型号,品牌等,都要求统计,后续模拟器玩家在活动发放,安全风控等方面也易于分析和把控。

实现

在网上搜了搜,大概思路是:

1:模拟器的cpu是x86,arm的,通过cpu信息判断

2:模拟器的传感器比较少,尤其没有光传感器等

3:模拟器没有蓝牙模块,可以通过蓝牙判断,这里没有考虑,毕竟需要动态权限

Manifest.permission.BLUETOOTH_CONNECT

在隐私合规的大环境下,还是尽量避免获取多的权限

4:通过部分特征参数,比如Build.FINGERPRINT、 Build.MODEL、Build.BRAND

5:通过模拟器特有文件检测

下面具体贴上工具类代码:

  1. package com.xx.xx.myapplication;
  2. import android.bluetooth.BluetoothAdapter;
  3. import android.content.Context;
  4. import android.content.pm.PackageManager;
  5. import android.hardware.Sensor;
  6. import android.hardware.SensorManager;
  7. import android.os.Build;
  8. import android.text.TextUtils;
  9. import android.util.Log;
  10. import java.io.BufferedReader;
  11. import java.io.File;
  12. import java.io.IOException;
  13. import java.io.InputStreamReader;
  14. import java.util.ArrayList;
  15. import java.util.List;
  16. public class EmutorUtils {
  17. private static final String TAG = "EmutorUtils";
  18. private static final String[] PKG_NAMES = {"com.mumu.launcher", "com.ami.duosupdater.ui", "com.ami.launchmetro",
  19. "com.ami.syncduosservices", "com.bluestacks.home", "com.bluestacks.windowsfilemanager",
  20. "com.bluestacks.settings", "com.bluestacks.bluestackslocationprovider", "com.bluestacks.appsettings",
  21. "com.bluestacks.bstfolder", "com.bluestacks.BstCommandProcessor", "com.bluestacks.s2p", "com.bluestacks.setup",
  22. "com.bluestacks.appmart", "com.kaopu001.tiantianserver", "com.kpzs.helpercenter", "com.kaopu001.tiantianime",
  23. "com.android.development_settings", "com.android.development", "com.android.customlocale2", "com.genymotion.superuser",
  24. "com.genymotion.clipboardproxy", "com.uc.xxzs.keyboard", "com.uc.xxzs", "com.blue.huang17.agent", "com.blue.huang17.launcher",
  25. "com.blue.huang17.ime", "com.microvirt.guide", "com.microvirt.market", "com.microvirt.memuime", "cn.itools.vm.launcher",
  26. "cn.itools.vm.proxy", "cn.itools.vm.softkeyboard", "cn.itools.avdmarket", "com.syd.IME", "com.bignox.app.store.hd",
  27. "com.bignox.launcher", "com.bignox.app.phone", "com.bignox.app.noxservice", "com.android.noxpush", "com.haimawan.push",
  28. "me.haima.helpcenter", "com.windroy.launcher", "com.windroy.superuser", "com.windroy.launcher", "com.windroy.ime",
  29. "com.android.flysilkworm", "com.android.emu.inputservice", "com.tiantian.ime", "com.microvirt.launcher", "me.le8.androidassist",
  30. "com.vphone.helper", "com.vphone.launcher", "com.duoyi.giftcenter.giftcenter"};
  31. private static final String[] FILES = {"/data/data/com.android.flysilkworm", "/data/data/com.bluestacks.filemanager"};
  32. public static String checkFeaturesByHardware(Context context) {
  33. String result = "";
  34. String hardware = getProperty("ro.hardware");
  35. if (null == hardware)
  36. return "unknown";
  37. String tempValue = hardware.toLowerCase();
  38. Log.d(TAG,tempValue);
  39. if(tempValue.startsWith("cancro")){
  40. result = "MUMU模拟器";
  41. }else if(tempValue.contains("nox")){
  42. result = "夜神模拟器";
  43. }else if(tempValue.equals("android_x86")){
  44. result= "雷电模拟器";
  45. }else{
  46. List pathList = getInstalledSimulatorPackages(context);
  47. result = getSimulatorBrand(pathList);
  48. }
  49. return result;
  50. }
  51. private static String getProperty(String propName) {
  52. String value = null;
  53. Object roSecureObj;
  54. try {
  55. roSecureObj = Class.forName("android.os.SystemProperties")
  56. .getMethod("get", String.class)
  57. .invoke(null, propName);
  58. if (roSecureObj != null) value = (String) roSecureObj;
  59. } catch (Exception e) {
  60. value = null;
  61. } finally {
  62. return value;
  63. }
  64. }
  65. private static List getInstalledSimulatorPackages(Context context) {
  66. ArrayList localArrayList = new ArrayList();
  67. try {
  68. for (int i = 0; i < PKG_NAMES.length; i++)
  69. try {
  70. context.getPackageManager().getPackageInfo(PKG_NAMES[i], PackageManager.GET_ACTIVITIES);
  71. localArrayList.add(PKG_NAMES[i]);
  72. } catch (PackageManager.NameNotFoundException localNameNotFoundException) {
  73. }
  74. if (localArrayList.size() == 0) {
  75. for (int i = 0; i < FILES.length; i++) {
  76. if (new File(FILES[i]).exists())
  77. localArrayList.add(FILES[i]);
  78. }
  79. }
  80. } catch (Exception e) {
  81. e.printStackTrace();
  82. }
  83. return localArrayList;
  84. }
  85. private static String getSimulatorBrand(List<String> list) {
  86. if (list.size() == 0)
  87. return "";
  88. String pkgName = list.get(0);
  89. if (pkgName.contains("mumu")) {
  90. return "mumu";
  91. } else if (pkgName.contains("ami")) {
  92. return "AMIDuOS";
  93. } else if (pkgName.contains("bluestacks")) {
  94. return "蓝叠";
  95. } else if (pkgName.contains("kaopu001") || pkgName.contains("tiantian")) {
  96. return "天天";
  97. } else if (pkgName.contains("kpzs")) {
  98. return "靠谱助手";
  99. } else if (pkgName.contains("genymotion")) {
  100. if (Build.MODEL.contains("iTools")) {
  101. return "iTools";
  102. } else if ((Build.MODEL.contains("ChangWan"))) {
  103. return "畅玩";
  104. } else {
  105. return "genymotion";
  106. }
  107. } else if (pkgName.contains("uc")) {
  108. return "uc";
  109. } else if (pkgName.contains("blue")) {
  110. return "blue";
  111. } else if (pkgName.contains("microvirt")) {
  112. return "逍遥";
  113. } else if (pkgName.contains("itools")) {
  114. return "itools";
  115. } else if (pkgName.contains("syd")) {
  116. return "手游岛";
  117. } else if (pkgName.contains("bignox")) {
  118. return "夜神";
  119. } else if (pkgName.contains("haimawan")) {
  120. return "海马玩";
  121. } else if (pkgName.contains("windroy")) {
  122. return "windroy";
  123. } else if (pkgName.contains("flysilkworm")) {
  124. return "雷电";
  125. } else if (pkgName.contains("emu")) {
  126. return "emu";
  127. } else if (pkgName.contains("le8")) {
  128. return "le8";
  129. } else if (pkgName.contains("vphone")) {
  130. return "vphone";
  131. } else if (pkgName.contains("duoyi")) {
  132. return "多益";
  133. }
  134. return "";
  135. }
  136. public static boolean isEmulator(Context context){
  137. return notHasLightSensorManager(context)
  138. ||isFeatures()
  139. ||checkIsNotRealPhone()
  140. ||checkPipes() ||isYeshenEmulator();
  141. }
  142. public static String getPhoneBrand(){
  143. return android.os.Build.BRAND;
  144. }
  145. public static String getPhoneModel(){
  146. return android.os.Build.MODEL;
  147. }
  148. /*
  149. *用途:判断蓝牙是否有效来判断是否为模拟器
  150. *返回:true 为模拟器
  151. */
  152. // private static boolean notHasBlueTooth() {
  153. // BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
  154. // if (ba == null) {
  155. // return true;
  156. // } else {
  157. // // 如果有蓝牙不一定是有效的。获取蓝牙名称,若为null 则默认为模拟器
  158. // String name = ba.getName();
  159. // if (TextUtils.isEmpty(name)) {
  160. // return true;
  161. // } else {
  162. // return false;
  163. // }
  164. // }
  165. // }
  166. /*
  167. *用途:依据是否存在光传感器来判断是否为模拟器
  168. *返回:true 为模拟器
  169. */
  170. private static Boolean notHasLightSensorManager(Context context) {
  171. SensorManager sensorManager = (SensorManager) context.getSystemService(context.SENSOR_SERVICE);
  172. Sensor sensor8 = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); //光
  173. if (null == sensor8) {
  174. return true;
  175. } else {
  176. return false;
  177. }
  178. }
  179. /*
  180. *用途:根据部分特征参数设备信息来判断是否为模拟器
  181. *返回:true 为模拟器
  182. */
  183. private static boolean isFeatures() {
  184. return Build.FINGERPRINT.startsWith("generic")
  185. || Build.FINGERPRINT.toLowerCase().contains("vbox")
  186. || Build.FINGERPRINT.toLowerCase().contains("test-keys")
  187. || Build.MODEL.contains("google_sdk")
  188. || Build.MODEL.contains("Emulator")
  189. || Build.MODEL.contains("Android SDK built for x86")
  190. || Build.MANUFACTURER.contains("Genymotion")
  191. || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
  192. || "google_sdk".equals(Build.PRODUCT);
  193. }
  194. /*
  195. *用途:根据CPU是否为电脑来判断是否为模拟器
  196. *返回:true 为模拟器
  197. */
  198. private static boolean checkIsNotRealPhone() {
  199. String cpuInfo = readCpuInfo();
  200. if ((cpuInfo.contains("intel") || cpuInfo.contains("amd"))) {
  201. return true;
  202. }
  203. return false;
  204. }
  205. /*
  206. *用途:根据CPU是否为电脑来判断是否为模拟器(子方法)
  207. *返回:String
  208. */
  209. private static String readCpuInfo() {
  210. String result = "";
  211. try {
  212. String[] args = {"/system/bin/cat", "/proc/cpuinfo"};
  213. ProcessBuilder cmd = new ProcessBuilder(args);
  214. Process process = cmd.start();
  215. StringBuffer sb = new StringBuffer();
  216. String readLine = "";
  217. BufferedReader responseReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
  218. while ((readLine = responseReader.readLine()) != null) {
  219. sb.append(readLine);
  220. }
  221. responseReader.close();
  222. result = sb.toString().toLowerCase();
  223. } catch (IOException ex) {
  224. }
  225. return result;
  226. }
  227. /*
  228. *用途:检测模拟器的特有文件
  229. *返回:true 为模拟器
  230. */
  231. private static String[] known_pipes = {"/dev/socket/qemud", "/dev/qemu_pipe"};
  232. private static boolean checkPipes() {
  233. for (int i = 0; i < known_pipes.length; i++) {
  234. String pipes = known_pipes[i];
  235. File qemu_socket = new File(pipes);
  236. if (qemu_socket.exists()) {
  237. Log.v("Result:", "Find pipes!");
  238. return true;
  239. }
  240. }
  241. Log.i("Result:", "Not Find pipes!");
  242. return false;
  243. }
  244. //****************适配夜神模拟器*******************
  245. //获取 cpu 信息
  246. private static String getCpuInfo() {
  247. String[] abis = new String[]{};
  248. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  249. abis = Build.SUPPORTED_ABIS;
  250. } else {
  251. abis = new String[]{Build.CPU_ABI, Build.CPU_ABI2};
  252. }
  253. StringBuilder abiStr = new StringBuilder();
  254. for (String abi : abis) {
  255. abiStr.append(abi);
  256. abiStr.append(',');
  257. }
  258. return abiStr.toString();
  259. }
  260. // 通过cpu判断是否模拟器 ,适配夜神
  261. private static boolean isYeshenEmulator() {
  262. String abiStr = getCpuInfo();
  263. if (abiStr != null && abiStr.length() > 0) {
  264. boolean isSupportX86 = false;
  265. boolean isSupportArm = false;
  266. if (abiStr.contains("x86_64") || abiStr.contains("x86")) {
  267. isSupportX86 = true;
  268. }
  269. if (abiStr.contains("armeabi") || abiStr.contains("armeabi-v7a") || abiStr.contains("arm64-v8a")) {
  270. isSupportArm = true;
  271. }
  272. if (isSupportX86 && isSupportArm) {
  273. //同时拥有X86和arm的判断为模拟器。
  274. return true;
  275. }
  276. }
  277. return false;
  278. }
  279. }

使用

  1. boolean flag = EmutorUtils.isEmulator(MainActivity.this);
  2. Toast.makeText(MainActivity.this,"是否是模拟器:"+flag,Toast.LENGTH_SHORT).show();
  3. button.setOnClickListener(new View.OnClickListener() {
  4. @Override
  5. public void onClick(View v) {
  6. String name = EmutorUtils.checkFeaturesByHardware(MainActivity.this);
  7. String brand = EmutorUtils.getPhoneBrand();
  8. String model = EmutorUtils.getPhoneModel();
  9. String result = "name:"+name+"brand:"+brand+"model:"+model;
  10. Toast.makeText(MainActivity.this,result,Toast.LENGTH_SHORT).show();
  11. }
  12. });

效果图

mumu

雷电

逍遥

夜神

官方AVD(用这个玩游戏基本没有)

参考 

http://dxtdbj.com/article.php?id=268
https://cloud.tencent.com/developer/article/2019963 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/932399
推荐阅读
相关标签
  

闽ICP备14008679号