如何检测Android应用程序何时在模拟器中运行?

时间:2010-05-09 20:40:29

标签: android android-emulator

我希望我的代码在模拟器上运行时的运行方式与在设备上运行时的运行方式略有不同。 (例如,使用10.0.2.2而不是公共URL自动针对开发服务器运行。)检测Android应用程序何时在模拟器中运行的最佳方法是什么?

39 个答案:

答案 0 :(得分:128)

这个解决方案怎么样:

public static boolean isEmulator() {
    return 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);
}

答案 1 :(得分:114)

一个常见的似乎是Build.FINGERPRINT.contains("generic")

答案 2 :(得分:64)

Android ID对我来说不起作用,我目前正在使用:

"google_sdk".equals( Build.PRODUCT );

答案 3 :(得分:31)

基于其他答案的提示,这可能是最强大的方式:

isEmulator = "goldfish".equals(Build.HARDWARE)

答案 4 :(得分:20)

如何使用以下代码判断您的应用是否使用调试密钥签名?它没有检测到模拟器,但它可能适用于您的目的?

public void onCreate Bundle b ) {
   super.onCreate(savedInstanceState);
   if ( signedWithDebugKey(this,this.getClass()) ) {
     blah blah blah
   }

  blah 
    blah 
      blah

}

static final String DEBUGKEY = 
      "get the debug key from logcat after calling the function below once from the emulator";    


public static boolean signedWithDebugKey(Context context, Class<?> cls) 
{
    boolean result = false;
    try {
        ComponentName comp = new ComponentName(context, cls);
        PackageInfo pinfo = context.getPackageManager().getPackageInfo(comp.getPackageName(),PackageManager.GET_SIGNATURES);
        Signature sigs[] = pinfo.signatures;
        for ( int i = 0; i < sigs.length;i++)
        Log.d(TAG,sigs[i].toCharsString());
        if (DEBUGKEY.equals(sigs[0].toCharsString())) {
            result = true;
            Log.d(TAG,"package has been signed with the debug key");
        } else {
            Log.d(TAG,"package signed with a key other than the debug key");
        }

    } catch (android.content.pm.PackageManager.NameNotFoundException e) {
        return false;
    }

    return result;

} 

答案 5 :(得分:12)

此代码适用于我

TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = tm.getNetworkOperatorName();
if("Android".equals(networkOperator)) {
    // Emulator
}
else {
    // Device
}

如果设备没有SIM卡,它会返回空字符串:“”

由于Android模拟器总是将“Android”作为网络运营商回归,我使用上面的代码。

答案 6 :(得分:11)

我尝试了几种技术,但确定了稍微修改后的版本,检查Build.PRODUCT,如下所示。这似乎从仿真器到仿真器有很大不同,这就是我目前拥有3个检查的原因。我想我可以检查一下product.contains(“sdk”),但认为下面的检查有点安全。

public static boolean isAndroidEmulator() {
    String model = Build.MODEL;
    Log.d(TAG, "model=" + model);
    String product = Build.PRODUCT;
    Log.d(TAG, "product=" + product);
    boolean isEmulator = false;
    if (product != null) {
        isEmulator = product.equals("sdk") || product.contains("_sdk") || product.contains("sdk_");
    }
    Log.d(TAG, "isEmulator=" + isEmulator);
    return isEmulator;
}

仅供参考 - 我发现我的Kindle Fire有Build.BRAND =“generic”,而且有些模拟器没有为网络运营商提供“Android”。

答案 7 :(得分:11)

以下两项都设置为“google_sdk”:

Build.PRODUCT
Build.MODEL

因此,使用以下任一行就足够了。

"google_sdk".equals(Build.MODEL)

"google_sdk".equals(Build.PRODUCT)

答案 8 :(得分:10)

我只是寻找_sdk_sdk_sdk_,甚至只是sdk Build.PRODUCT中的if(Build.PRODUCT.matches(".*_?sdk_?.*")){ //-- emulator -- }else{ //-- other device -- }

{{1}}

答案 9 :(得分:9)

我从来没有找到一个很好的方法来判断你是否在模拟器中。

但如果您只是需要检测是否处于开发环境中,则可以执行此操作:

     if(Debug.isDebuggerConnected() ) {
        // Things to do in debug environment...
    }

希望这有帮助......

答案 10 :(得分:7)

这是我的解决方案(仅当您在调试计算机上运行Web服务器时才有效): 我创建了一个后台任务,它在应用程序启动时启动。它查找http://10.0.2.2,如果它存在,则将全局参数(IsDebug)更改为true。这是一种无声的方式来找出你在哪里跑。

public class CheckDebugModeTask extends AsyncTask<String, Void, String> {
public static boolean IsDebug = false;

public CheckDebugModeTask()
{

}

@Override
protected String doInBackground(String... params) {     
  try {
    HttpParams httpParameters = new BasicHttpParams();
    int timeoutConnection = 1000;
    HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
    int timeoutSocket = 2000;
    HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

    String url2 = "http://10.0.2.2";        
          HttpGet httpGet = new HttpGet(url2);
    DefaultHttpClient client = new DefaultHttpClient(httpParameters);

    HttpResponse response2 = client.execute(httpGet);
    if (response2 == null || response2.getEntity() == null || response2.getEntity().getContent() == null)
    return "";

    return "Debug";

} catch (Exception e) {
    return "";
}
}

@Override
protected void onPostExecute (String result)
{       
if (result == "Debug")
{
    CheckDebugModeTask.IsDebug = true;
}
}

来自主要活动onCreate:

CheckDebugModeTask checkDebugMode = new CheckDebugModeTask();
checkDebugMode.execute("");

答案 11 :(得分:7)

不知道是否有更好的方法来检测emu,但模拟器将在根目录中包含文件init.goldfish.rc

这是模拟器特定的启动脚本,它不应该存在于非模拟器构建中。

答案 12 :(得分:7)

使用此功能:

 public static final boolean isEmulator() {

    int rating = 0;

    if ((Build.PRODUCT.equals("sdk")) || (Build.PRODUCT.equals("google_sdk"))
            || (Build.PRODUCT.equals("sdk_x86")) || (Build.PRODUCT.equals("vbox86p"))) {
        rating++;
    }
    if ((Build.MANUFACTURER.equals("unknown")) || (Build.MANUFACTURER.equals("Genymotion"))) {
        rating++;
    }
    if ((Build.BRAND.equals("generic")) || (Build.BRAND.equals("generic_x86"))) {
        rating++;
    }
    if ((Build.DEVICE.equals("generic")) || (Build.DEVICE.equals("generic_x86")) || (Build.DEVICE.equals("vbox86p"))) {
        rating++;
    }
    if ((Build.MODEL.equals("sdk")) || (Build.MODEL.equals("google_sdk"))
            || (Build.MODEL.equals("Android SDK built for x86"))) {
        rating++;
    }
    if ((Build.HARDWARE.equals("goldfish")) || (Build.HARDWARE.equals("vbox86"))) {
        rating++;
    }
    if ((Build.FINGERPRINT.contains("generic/sdk/generic"))
            || (Build.FINGERPRINT.contains("generic_x86/sdk_x86/generic_x86"))
            || (Build.FINGERPRINT.contains("generic/google_sdk/generic"))
            || (Build.FINGERPRINT.contains("generic/vbox86p/vbox86p"))) {
        rating++;
    }

    return rating > 4;

    }

答案 13 :(得分:5)

一种方法中的所有答案

static boolean checkEmulator()
{
    try
    {
        String buildDetails = (Build.FINGERPRINT + Build.DEVICE + Build.MODEL + Build.BRAND + Build.PRODUCT + Build.MANUFACTURER + Build.HARDWARE).toLowerCase();

        if (buildDetails.contains("generic") 
        ||  buildDetails.contains("unknown") 
        ||  buildDetails.contains("emulator") 
        ||  buildDetails.contains("sdk") 
        ||  buildDetails.contains("genymotion") 
        ||  buildDetails.contains("x86") // this includes vbox86
        ||  buildDetails.contains("goldfish")
        ||  buildDetails.contains("test-keys"))
            return true;
    }   
    catch (Throwable t) {Logger.catchedError(t);}

    try
    {
        TelephonyManager    tm  = (TelephonyManager) App.context.getSystemService(Context.TELEPHONY_SERVICE);
        String              non = tm.getNetworkOperatorName().toLowerCase();
        if (non.equals("android"))
            return true;
    }
    catch (Throwable t) {Logger.catchedError(t);}

    try
    {
        if (new File ("/init.goldfish.rc").exists())
            return true;
    }
    catch (Throwable t) {Logger.catchedError(t);}

    return false;
}

答案 14 :(得分:5)

从电池,模拟器: 电源始终是AC充电器。 温度始终为0.

您可以使用Build.HOST来记录主机值,不同的模拟器具有不同的主机值。

答案 15 :(得分:5)

我找到了新的模拟器Build.HARDWARE = "ranchu"

参考:https://groups.google.com/forum/#!topic/android-emulator-dev/dltBnUW_HzU

而且我发现了Android官方方式来检查是否是模拟器。我认为这对我们来说是个很好的参考。

  

自Android API Level 23 [Android 6.0]

以来
package com.android.internal.util;

/**
 * @hide
 */
public class ScreenShapeHelper {
    private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
}

我们ScreenShapeHelper.IS_EMULATOR检查是否是模拟器。

  

自Android API Level 24 [Android 7.0]

以来
package android.os;

/**
 * Information about the current build, extracted from system properties.
 */
public class Build {


    /**
     * Whether this build was for an emulator device.
     * @hide
     */
    public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");

}

我们Build.IS_EMULATOR检查是否是模拟器。

官方检查仿真器是否不是新的方式,也可能还不够,上面的答案也提到了。

但这可能告诉我们官方将提供官方检查是否是模拟器的方式。

正如使用上面提到的所有方法,现在我们还可以使用两种方式来检查是否模拟器。

How to access the com.android.internal package and @hide

等待官方开放SDK。

答案 16 :(得分:5)

我的推荐

从github尝试this

易于检测android模拟器

  
      
  • 已在Device Farm(https://aws.amazon.com/device-farm/)中的真实设备上检查过
  •   
  • BlueStacks
  •   
  • Genymotion
  •   
  • Android模拟器
  •   
  • 安迪46.2.207.0
  •   
  • MEmu播放
  •   
  • Nox应用程序播放器
  •   
  • Koplayer
  •   
  • .....
  •   

如何与示例一起使用:

EmulatorDetector.with(this)
                .setCheckTelephony(true)
                .addPackageName("com.bluestacks")
                .setDebug(true)
                .detect(new EmulatorDetector.OnEmulatorDetectorListener() {
                    @Override
                    public void onResult(boolean isEmulator) {

                    }
                });

答案 17 :(得分:5)

以上建议的检查ANDROID_ID的解决方案为我工作,直到我今天更新到Android 2.2发布的最新SDK工具。

因此,我目前切换到以下解决方案,但到目前为止还有一个缺点,即您需要将PHONE_STATE读取权限(<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

private void checkForDebugMode() {
    ISDEBUGMODE = false; //(Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID) == null);

    TelephonyManager man = (TelephonyManager) getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
    if(man != null){
        String devId = man.getDeviceSoftwareVersion();
        ISDEBUGMODE = (devId == null);
    }
} 

答案 18 :(得分:5)

另一种选择是查看ro.hardware属性并查看它是否设置为goldfish。不幸的是,似乎没有一种简单的方法可以从Java中做到这一点,但是使用property_get()从C中做到这一点很简单。

答案 19 :(得分:5)

Google使用Flutter的device-info plugin中的以下代码来确定设备是否为模拟器:

private boolean isEmulator() {
    return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
        || Build.FINGERPRINT.startsWith("generic")
        || Build.FINGERPRINT.startsWith("unknown")
        || Build.HARDWARE.contains("goldfish")
        || Build.HARDWARE.contains("ranchu")
        || Build.MODEL.contains("google_sdk")
        || Build.MODEL.contains("Emulator")
        || Build.MODEL.contains("Android SDK built for x86")
        || Build.MANUFACTURER.contains("Genymotion")
        || Build.PRODUCT.contains("sdk_google")
        || Build.PRODUCT.contains("google_sdk")
        || Build.PRODUCT.contains("sdk")
        || Build.PRODUCT.contains("sdk_x86")
        || Build.PRODUCT.contains("vbox86p")
        || Build.PRODUCT.contains("emulator")
        || Build.PRODUCT.contains("simulator");
}

答案 20 :(得分:4)

你可以查看IMEI#, http://developer.android.com/reference/android/telephony/TelephonyManager.html#getDeviceId%28%29

如果我在模拟器上回忆这返回0.但是,没有文件我能找到保证。虽然模拟器可能并不总是返回0,但是看起来非常安全,注册的手机不会返回0.在非手机Android设备上会发生什么,或者没有安装SIM卡或者当前未注册的设备网络

这似乎是一个坏主意,依赖于此。

这也意味着您需要请求获得阅读电话状态的许可,如果您还没有其他要求,这是不好的。

如果不是这样,那么在您最终生成已签名的应用之前,总会在某处稍微翻一番。

答案 21 :(得分:4)

Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")

如果应用程序在模拟器上运行,则应该返回true。

我们应该注意的是没有检测到所有模拟器,因为只有几个不同的模拟器。这很容易检查。 我们必须确保不会将实际设备检测为模拟器。

我使用名为“Android Device Info Share”的应用来检查此内容。

在这个应用程序中,您可以看到许多设备的各种信息(可能是世界上大多数设备;如果列表中缺少您正在使用的设备,它将自动添加)。

答案 22 :(得分:3)

我已经收集了关于这个问题的所有答案,并提出了检测Android是否在虚拟机/模拟器上运行的功能:

public boolean isvm(){


        StringBuilder deviceInfo = new StringBuilder();
        deviceInfo.append("Build.PRODUCT " +Build.PRODUCT +"\n");
        deviceInfo.append("Build.FINGERPRINT " +Build.FINGERPRINT+"\n");
        deviceInfo.append("Build.MANUFACTURER " +Build.MANUFACTURER+"\n");
        deviceInfo.append("Build.MODEL " +Build.MODEL+"\n");
        deviceInfo.append("Build.BRAND " +Build.BRAND+"\n");
        deviceInfo.append("Build.DEVICE " +Build.DEVICE+"\n");
        String info = deviceInfo.toString();


        Log.i("LOB", info);


        Boolean isvm = false;
        if(
                "google_sdk".equals(Build.PRODUCT) ||
                "sdk_google_phone_x86".equals(Build.PRODUCT) ||
                "sdk".equals(Build.PRODUCT) ||
                "sdk_x86".equals(Build.PRODUCT) ||
                "vbox86p".equals(Build.PRODUCT) ||
                Build.FINGERPRINT.contains("generic") ||
                Build.MANUFACTURER.contains("Genymotion") ||
                Build.MODEL.contains("Emulator") ||
                Build.MODEL.contains("Android SDK built for x86")
                ){
            isvm =  true;
        }


        if(Build.BRAND.contains("generic")&&Build.DEVICE.contains("generic")){
            isvm =  true;
        }

        return isvm;
    }

在模拟器,Genymotion和Bluestacks上测试(2015年10月1日)。

答案 23 :(得分:3)

将文件放入模拟器的文件系统中;由于该文件不会存在于真实设备上,因此该文件应该稳定,可靠且易于修复。

答案 24 :(得分:3)

这对我有用

public boolean isEmulator() {
    return Build.MANUFACTURER.equals("unknown");
}

答案 25 :(得分:3)

实际上,2.2上的ANDROID_ID总是等于 9774D56D682E549C (根据this thread +我自己的实验)。

所以,你可以检查一下这样的东西:

String androidID = ...;
if(androidID == null || androidID.equals("9774D56D682E549C"))
    do stuff;

不是最漂亮的,但它确实起作用了。

答案 26 :(得分:2)

检查答案时,使用LeapDroid,Droid4x或Andy仿真器时没有一个工作,

对所有情况的作用如下:

 private static String getSystemProperty(String name) throws Exception {
    Class systemPropertyClazz = Class.forName("android.os.SystemProperties");
    return (String) systemPropertyClazz.getMethod("get", new Class[]{String.class}).invoke(systemPropertyClazz, new Object[]{name});
}

public boolean isEmulator() {
    boolean goldfish = getSystemProperty("ro.hardware").contains("goldfish");
    boolean emu = getSystemProperty("ro.kernel.qemu").length() > 0;
    boolean sdk = getSystemProperty("ro.product.model").equals("sdk");
    return goldfish || emu || sdk;
}

答案 27 :(得分:1)

if (Build.BRAND.equalsIgnoreCase("generic")) {
    // Is the emulator
}

所有BUILD引用都是build.prop值,所以你必须考虑如果你要将它放入发布代码中,你可能会有一些root用户因为某种原因修改了它们。除非特别试图模拟仿真器,否则几乎不需要使用generic作为品牌的修改。

指纹是构建编译和内核编译签名。有些版本使用泛型,通常直接来自Google。

在已修改的设备上,IMEI也有可能被清零,因此除非您完全阻止修改后的设备,否则这是不可靠的。

Goldfish是所有其他设备扩展的基础android构建。每个Android设备都有一个init.goldfish.rc,除非因未知原因被黑客入侵和删除。

答案 28 :(得分:1)

由于Genymotion的底层仿真引擎是VirtualBox,并且不会很快改变,我发现以下代码最可靠:

   public static boolean isGenymotion() {
        return Build.PRODUCT != null && Build.PRODUCT.contains("vbox");
}

答案 29 :(得分:1)

if ("sdk".equals( Build.PRODUCT )) {
 // Then you are running the app on the emulator.
        Log.w("MyAPP", "\n\n  Emulator \n\n"); 
}

答案 30 :(得分:1)

无论您使用哪种代码进行模拟器检测,我强烈建议您编写单元测试,以涵盖您依赖的所有Build.FINGERPRINTBuild.HARDWAREBuild.MANUFACTURER值。以下是一些示例测试:

@Test
public void testIsEmulatorGenymotion() throws Exception {
    assertThat(
            DeviceUtils.isRunningOnEmulator(
                    "generic/vbox86p/vbox86p:4.1.1/JRO03S/eng.buildbot.20150217.102902:userdebug/test-keys",
                    "vbox86", "Genymotion")).isTrue();

    assertThat(
            DeviceUtils.isRunningOnEmulator(
                    "generic/vbox86p/vbox86p:5.1/LMY47D/buildbot06092001:userdebug/test-keys", "vbox86",
                    "Genymotion")).isTrue();
}

@Test
public void testIsEmulatorDefaultAndroidEmulator() throws Exception {
    assertThat(
            DeviceUtils.isRunningOnEmulator(
                    "generic_x86/sdk_google_phone_x86/generic_x86:5.0.2/LSY66H/1960483:eng/test-keys", "goldfish",
                    "unknown")).isTrue();

    assertThat(
            DeviceUtils.isRunningOnEmulator(
                    "Android/sdk_google_phone_x86_64/generic_x86_64:6.0/MASTER/2469028:userdebug/test-keys",
                    "ranchu", "unknown")).isTrue();
}

@Test
public void testIsEmulatorRealNexus5() throws Exception {
    assertThat(
            DeviceUtils.isRunningOnEmulator("google/hammerhead/hammerhead:6.0.1/MMB29K/2419427:user/release-keys",
                    "hammerhead", "LGE")).isFalse();
}

...这里是我们的代码(为了简洁而删除了调试日志和注释):

public static boolean isRunningOnEmulator() {
    if (sIsRunningEmulator == null) {
        sIsRunningEmulator = isRunningOnEmulator(Build.FINGERPRINT, Build.HARDWARE, Build.MANUFACTURER);
    }

    return sIsRunningEmulator;
}

static boolean isRunningOnEmulator(String fingerprint, String hardware, String manufacturer) {
    boolean isEmulatorFingerprint = fingerprint.endsWith("test-keys");
    boolean isEmulatorManufacturer = manufacturer.equals("Genymotion")
            || manufacturer.equals("unknown");

    if (isEmulatorFingerprint && isEmulatorManufacturer) {
        return true;
    } else {
        return false;
    }
}

答案 31 :(得分:0)

这个方法对我有用

    public static boolean isRunningOnEmulator(String fingerprint, String hardware, String manufacturer) {
    boolean isEmulatorFingerprint = fingerprint.endsWith("test-keys");
    boolean isHardware = hardware.toLowerCase().contains("intel") || hardware.toLowerCase().contains("vbox");
    boolean isEmulatorManufacturer = manufacturer.equals("Genymotion")
            || manufacturer.equals("unknown");

    if (isHardware || isEmulatorFingerprint && isEmulatorManufacturer) {
        return true;
    } else {
        return false;
    }
}

答案 32 :(得分:0)

试试这个方法。

在 Google 和 Genymotion 模拟器上测试。

public Boolean IsVM() {
    String radioVersion = android.os.Build.getRadioVersion();
    return radioVersion == null || radioVersion.isEmpty() || radioVersion.equals("1.0.0.0");
}

答案 33 :(得分:0)

最常用的技术是匹配来自品牌、名称等的值。但这种方法是静态的,并且适用于有限版本的模拟器。如果有 1000 多家 VM 制造商会怎样?那么你必须编写一个代码来匹配 1000 多个虚拟机?

但是浪费时间。即使过了一段时间,也会有新的其他虚拟机启动,您的脚本也会被浪费。

所以根据我的测试,我知道了 getRadioVersion() 在 VM 上返回空, 并返回真实安卓设备上的版本号。

public Boolean IsVM() 
{
    return android.os.Build.getRadioVersion().length() == 0;
} 
//return true if VM
//return false if real

虽然有效,但我对此没有官方解释。

代码: http://github.com/Back-X/anti-vm/blob/main/android/anti-vm.b4a

发布: http://github.com/Back-X/anti-vm/releases/download/1/anti-vm.apk

答案 34 :(得分:0)

Firebase Crashlytics 是这样处理的:

  public static boolean isEmulator(Context context) {
      final String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
      return Build.PRODUCT.contains(SDK)
          || Build.HARDWARE.contains(GOLDFISH)
          || Build.HARDWARE.contains(RANCHU)
          || androidId == null;
  }

答案 35 :(得分:0)

最好验证设备是否安装了以下软件包:

    mListPackageName.add("com.google.android.launcher.layouts.genymotion");
    mListPackageName.add("com.bluestacks");
    mListPackageName.add("com.vphone.launcher");
    mListPackageName.add("com.bignox.app");

然后简单地对软件包管理器进行检查,直到找到一个。

private static boolean isEmulByPackage(Context context) {
        final PackageManager pm = context.getPackageManager();
        for (final String pkgName : mListPackageName) {
            return isPackageInstalled(pkgName, pm);
        }
        return false;
    }

private static boolean isPackageInstalled(final String packageName, final PackageManager packageManager) {
    try {
        packageManager.getPackageInfo(packageName, 0);
        return true;
    } catch (PackageManager.NameNotFoundException e) {
        return false;
    }
}

答案 36 :(得分:0)

尝试从github获得此链接。

https://github.com/mofneko/EmulatorDetector

此模块可帮助您对支持Unity的Android项目进行仿真检测。

基本检查器

  • BlueStacks
  • Genymotion
  • Android模拟器
  • Nox应用程序播放器
  • Koplayer
  • .....

答案 37 :(得分:0)

这对我有用,而不是startsWithBuild.FINGERPRINT.contains("generic")

有关此问题,请访问:https://community.oracle.com/docs/DOC-931127

答案 38 :(得分:-1)

您可以检查deviceId(IMEI)是否为“000000000000000”(15个零)