Làm cách nào để phát hiện khi ứng dụng Android đang chạy trong trình giả lập?


313

Tôi muốn có mã của tôi chạy hơi khác khi chạy trên trình giả lập so với khi chạy trên thiết bị. ( Ví dụ: sử dụng 10.0.2.2 thay vì URL công khai để chạy tự động đối với máy chủ phát triển.) Cách tốt nhất để phát hiện khi ứng dụng Android đang chạy trong trình giả lập là gì?


2
Có thể có một cái nhìn tại android.os.Build.
yanchenko

11
Làm tôi ngạc nhiên ... Google nên có một cách tiêu chuẩn để làm điều này?
bột366

@kreker vấn đề là gì, bạn đang phải đối mặt trong các giải pháp hiện có?
Khemraj

@Khemraj vấn đề gian lận. Kẻ ác có thể chế nhạo một số cảm biến và thay đổi một số chuỗi để giả vờ thiết bị thực
kreker

Câu trả lời:


159

Làm thế nào về giải pháp này:

    fun isProbablyAnEmulator() = 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.BOARD == "QC_Reference_Phone" //bluestacks
            || Build.MANUFACTURER.contains("Genymotion")
            || Build.HOST.startsWith("Build") //MSI App Player
            || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
            || "google_sdk" == Build.PRODUCT

Lưu ý rằng một số trình giả lập thông số kỹ thuật chính xác của thiết bị thật, vì vậy có thể không thể phát hiện ra.

Ở đây một đoạn nhỏ bạn có thể tạo trong APK để hiển thị nhiều thứ khác nhau về nó, vì vậy bạn có thể thêm các quy tắc của riêng mình:

        textView.text = "FINGERPRINT:${Build.FINGERPRINT}\n" +
                "MODEL:${Build.MODEL}\n" +
                "MANUFACTURER:${Build.MANUFACTURER}\n" +
                "BRAND:${Build.BRAND}\n" +
                "DEVICE:${Build.DEVICE}\n" +
                "BOARD:${Build.BOARD}\n" +
                "HOST:${Build.HOST}\n" +
                "PRODUCT:${Build.PRODUCT}\n"

9
Đó là cách Facebook phát hiện các trình giả lập trong React-Native
Vaiden

Đây là những gì tôi đã phải cập nhật sau khi sử dụng câu trả lời từ @Aleadam trong một thời gian ngắn (nó đã ngừng hoạt động đối với tôi).
ckbhodge

@Sid Nên thêm cái gì cho nó?
nhà phát triển Android

2
@Sid Bạn đã in ra các biến lớp Build khác nhau ở đó chưa? Không có gì đặc biệt? Bạn đã thử cái này chưa: github.com/framgia/android-emulator-detector ?
nhà phát triển Android

1
@DrDeo Bạn có thể thêm kiểm tra bản dựng hiện tại bằng BuildConfig.DEBUG hoặc tạo bản dựng của riêng bạn với biến tùy chỉnh của riêng bạn. Bạn cũng có thể sử dụng Proguard để làm cho chức năng này luôn trả về false hoặc một cái gì đó (ví dụ: bạn có thể xóa nhật ký, như được hiển thị ở đây: Medium.com/tixdo-labs/iêu , vì vậy có lẽ điều đó cũng có thể xảy ra)
nhà phát triển Android

118

Một người chung có thể là Build.FINGERPRINT.contains("generic")


Điều này hoạt động ngay cả với Trình giả lập Galaxy Tab. Câu trả lời thích hàng đầu đã không.
BufferStack

10
Vui lòng cho biết liệu dấu vân tay có chứa "chung" là trình giả lập hay thiết bị. Thông tin đó là chính nhưng không được cung cấp.
James Cameron

2
Trình giả lập - đánh giá bằng các bình luận trước bạn :)
Dori

7
Điều này trả về đúng trên các thiết bị của tôi chạy CyanogenMod vì vậy hãy cẩn thận.
ardevd

8
Các tài liệu Android nói rằng bạn không nên cố gắng để giải thích các FINGERPRINTgiá trị.
gnuf

64

Vâng, id Android không hoạt động với tôi, tôi hiện đang sử dụng:

"google_sdk".equals( Build.PRODUCT );

35
Bất cứ ai đọc điều này có thể quan tâm để biết rằng chuỗi này dường như đã thay đổi thành 'sdk', thay vì 'google_sdk'.
Daniel Sloof

15
@Daniel: Tôi sử dụng 2.3.3 với Google API và thông báo 'google_sdk'. Có vẻ như đó là 'google_sdk' cho AVD với Google API và 'sdk' cho những người bình thường.
Randy Sugianto 'Yuku'

3
Trình giả lập Intel trả về "full_x86" vì vậy tôi sẽ không dựa vào phương pháp này.
dùng462982

3
@GlennMaynard Hình thức đảo ngược là xấu, nhưng thực tế: Build.PRODVEL có thể là null trong khi "google_sdk" không thể, do đó hình thức này tránh được lỗi tham chiếu null tiềm năng.
Rupert Rawnsley

4
Bao gồm nhiều trường hợp hơn: "google_sdk" .equals (Build.PRODVEL) || "sdk" .equals (Build.PRODVEL) || "sdk_x86" .equals (Build.PRODVEL) || "vbox86p" .equals (Build.PRODVEL)
Alberto Alonso Ruibal

31

Dựa trên gợi ý từ các câu trả lời khác, đây có lẽ là cách mạnh mẽ nhất:

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


Đúng. Không giống như Build.PRODVEL, Build.HARDWARE (cá vàng) giống với SDK và AOSP chính thức. Tuy nhiên, trước API 8, bạn phải sử dụng sự phản chiếu để có được tại trường PHẦN MỀM, tuy nhiên.
David Chandler

4
Tôi sẽ đi cùngisEmulator = Build.HARDWARE.contains("golfdish")
holmes

7
@holmes: typo, s / b "cá vàng"

7
Đối với hình ảnh Android 5.1 x86_64 (và có thể là các hình ảnh 64 bit gần đây khác) sẽ là "ranchu" thay vì "cá vàng".
warbi

28

Google sử dụng mã này trong plugin thông tin thiết bị từ Flutter để xác định xem thiết bị có phải là trình giả lập hay không:

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

Làm thế nào về một cái gì đó giống như mã dưới đây để biết liệu ứng dụng của bạn đã được ký bằng khóa gỡ lỗi chưa? Nó không phát hiện ra trình giả lập nhưng nó có thể hoạt động cho mục đích của bạn?

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;

} 

1
Cảm ơn bạn cho mã này. Tôi đã kiểm tra và nó đang hoạt động, việc xử lý khóa gỡ lỗi dài có thể gây đau đớn nhưng nó chỉ được thực hiện một lần. Đây là giải pháp đáng tin cậy duy nhất , vì tất cả các câu trả lời khác đều so sánh một phần của chuỗi thông tin xây dựng hệ điều hành với chuỗi tĩnh và điều này có thể và đã được thay đổi qua các phiên bản SDK Android và cũng có thể được giả mạo bởi các bản dựng Android tùy chỉnh.
ZoltanF

Tôi nghĩ đó là giải pháp đáng tin cậy duy nhất. Tuy nhiên, khóa gỡ lỗi có thể thay đổi nhanh hơn chúng ta muốn.
rds

2
Một cách tốt hơn để làm điều này là BuildConfig.DEBUG.
Mygod

13

Mã này làm việc cho tôi

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

Trong trường hợp thiết bị đó không có thẻ sim, Nó sẽ rút lại chuỗi trống: ""

Vì trình giả lập Android luôn truy xuất "Android" với tư cách là nhà điều hành mạng, tôi sử dụng mã ở trên.


3
Thiết bị không có thẻ SIM (chẳng hạn như máy tính bảng) sẽ trả về những gì?
rds

Chạy trình giả lập cho Android 2.1. Mã này đã hoạt động với tôi, nhưng kể từ khi nâng cấp Cordova lên 2.7.0, biến Bối cảnh dường như không được xác định hoặc một cái gì đó. Đây là lỗi tôi gặp phải trong ADT: "Bối cảnh không thể được giải quyết thành một biến." Ngoài ra, theo nhận xét ở trên, đây KHÔNG phải là một phương pháp đáng tin cậy (mặc dù tôi thực sự không có nó tự thất bại).
Rustavore

2
@rds Các thiết bị không có thẻ SIM sẽ trả về chuỗi trống ("")
JJ Kim

Có cách nào để có giá trị này với trình giả lập không? bởi vì tôi muốn chặn tất cả người dùng nếu họ không có thẻ sim.
c-an

11

Cả hai mục sau được đặt thành "google_sdk":

Build.PRODUCT
Build.MODEL

Vì vậy, nó là đủ để sử dụng một trong những dòng sau.

"google_sdk".equals(Build.MODEL)

hoặc là

"google_sdk".equals(Build.PRODUCT)

Khi chạy trình giả lập x86 trên Windows, Build.Sản phẩm là sdk_x86.
Edward Brey

kiểm tra với SẢN PHẨM không phải là một lựa chọn tốt vì nó trả về các giá trị khác nhau từ các trình giả lập khác nhau
Beeing Jk

11

Tôi đã thử một số kỹ thuật, nhưng đã giải quyết một phiên bản sửa đổi một chút về việc kiểm tra Build.PRODVEL như bên dưới. Điều này dường như thay đổi khá nhiều từ trình giả lập sang trình giả lập, đó là lý do tại sao tôi có 3 kiểm tra hiện tại tôi có. Tôi đoán tôi có thể vừa kiểm tra nếu sản phẩm.contains ("sdk") nhưng nghĩ rằng kiểm tra dưới đây an toàn hơn một chút.

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;
}

FYI - Tôi thấy rằng Kindle Fire của tôi có Build.BRAND = "generic" và một số trình giả lập không có "Android" cho nhà điều hành mạng.


10

Tôi chỉ tìm kiếm _sdk, _sdk_hoặc sdk_, hoặc thậm chí chỉ là sdkmột phần trong Build.PRODUCT:

if(Build.PRODUCT.matches(".*_?sdk_?.*")){
  //-- emulator --
}else{
  //-- other device --
}

3
Tại sao không chỉ contains("sdk")? Sự khác biệt duy nhất (ngoài việc nhanh hơn) là matches(".*_?sdk_?.*")yêu cầu rằng nếu có một ký tự trước hoặc sau sdk, thì đó phải là dấu gạch dưới '_', đây không phải là điều quan trọng cần kiểm tra.
Nulano

9

Tôi không bao giờ tìm thấy một cách hay để nói nếu bạn đang ở trong trình giả lập.

nhưng nếu bạn chỉ cần dò tìm nếu bạn ở trong môi trường phát triển, bạn có thể làm điều này:

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

Hy vọng điều này giúp đỡ....


8

sử dụng chức năng này:

 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;

    }

7

Không biết có cách nào tốt hơn để phát hiện emu không, nhưng trình giả lập sẽ có tệp init.goldfish.rc trong thư mục gốc.

Đó là tập lệnh khởi động cụ thể của trình giả lập và nó không nên có trên bản dựng không giả lập.


Trong quá trình khởi động hệ thống Android, nhân Linux trước tiên gọi tiến trình là "init". init đọc các tập tin "/init.rc" và "init.device.rc". "init.device.rc" là thiết bị cụ thể, trên thiết bị ảo, tệp này được gọi là "init.goldfish.rc".
NET3

7

Đây là giải pháp của tôi (nó chỉ hoạt động nếu bạn chạy máy chủ web trên máy gỡ lỗi của mình): Tôi đã tạo một tác vụ nền bắt đầu khi ứng dụng khởi động. Nó tìm kiếm http://10.0.2.2 và nếu nó tồn tại, nó thay đổi một tham số toàn cầu (IsDebug) thành true. Đó là một cách im lặng để tìm ra nơi bạn đang chạy.

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;
}
}

từ hoạt động chính trênCreate:

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

7

Từ Pin, trình giả lập: Nguồn điện luôn là Bộ sạc AC. Nhiệt độ luôn bằng 0.

Và bạn có thể sử dụng Build.HOSTđể ghi lại giá trị máy chủ, trình giả lập khác nhau có giá trị máy chủ khác nhau.


Làm thế nào để bạn có được nguồn năng lượng và nhiệt độ?
nhà phát triển Android

5

Một lựa chọn khác là xem xét thuộc tính ro.hardware và xem nếu nó được đặt thành cá vàng. Thật không may, dường như không có một cách dễ dàng để làm điều này từ Java nhưng nó không quan trọng bằng C bằng property_get () .


4
Điều này dường như để làm việc từ NDK. Bao gồm <sys / system_properIES.h> và sử dụng __system_property_get ("ro.hardware", buf) sau đó kiểm tra xem buf có phải là "cá vàng" không.
NuSkooler

5

Các giải pháp được đề xuất ở trên để kiểm tra ANDROID_ID việc cho tôi cho đến khi tôi cập nhật hôm nay lên các công cụ SDK mới nhất được phát hành với Android 2.2.

Do đó, hiện tại tôi đã chuyển sang giải pháp sau hoạt động với nhược điểm tuy nhiên bạn cần đặt quyền đọc 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);
    }
} 

5

Tất cả các câu trả lời trong một phương pháp

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;
}

Đẹp một. init.goldfish.rcchỉ tồn tại trong trình giả lập; đó cũng là một kiểm tra tốt trong tương lai cùng với các chi tiết Xây dựng.
sud007

2
@ sud007 Có nhiều thiết bị ngoài kia với `/init.goldfish.rc và điều này sẽ dẫn đến kết quả dương tính giả. Ví dụ, nhiều thiết bị dòng Samsung Galaxy.
laalto

@laalto bạn đã thực sự chính xác. Tôi thấy rằng sau đó xin lỗi và tôi quên cập nhật nó ở đây.
sud007

khóa kiểm tra đã tạo ra dương tính giả cho tôi.
Avi Parshan

Trên thiết bị nào họ đang tạo ra dương tính giả?
Aman Verma

5

Tôi tìm thấy trình giả lập mới Build.HARDWARE = "ranchu".

Tài liệu tham khảo: https://groups.google.com/forum/#!topic/android-emulator-dev/dltBnUW_HzU

Và tôi cũng tìm thấy cách chính thức của Android để kiểm tra xem trình giả lập hay không. Tôi nghĩ đó là tài liệu tham khảo tốt cho chúng tôi.

Kể từ API Android cấp 23 [Android 6.0]

package com.android.internal.util;

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

Chúng ta phải ScreenShapeHelper.IS_EMULATORkiểm tra xem trình giả lập.

Kể từ API Android cấp 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");

}

Chúng ta có Build.IS_EMULATORkiểm tra xem trình giả lập.

Cách chính thức kiểm tra xem trình giả lập không phải là mới, và cũng có thể không đủ, các câu trả lời ở trên cũng được đề cập.

Nhưng điều này có thể cho chúng ta thấy rằng quan chức sẽ cung cấp cách thức chính thức để kiểm tra xem trình giả lập hay không.

Như sử dụng tất cả các cách đã đề cập ở trên, ngay bây giờ chúng ta cũng có thể sử dụng hai cách để kiểm tra xem trình giả lập.

Cách truy cập com.android.internalgói và@hide

và chờ SDK mở chính thức.


5

Đề nghị của tôi:

Hãy thử điều này từ github.

Dễ dàng phát hiện trình giả lập Android

  • Đã kiểm tra trên các thiết bị thực trong Trang trại thiết bị ( https://aws.amazon.com/device-farm/ )
  • BlueStacks
  • Genymotion
  • Trình giả lập Android
  • Andy 46.2.207.0
  • MEmu chơi
  • Trình phát ứng dụng Nox
  • Koplayer
  • .....

Cách sử dụng với một ví dụ:

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

                    }
                });

4

bạn có thể kiểm tra IMEI #, http://developer.android.com/reference/android/telephony/TelephonyManager.html#getDeviceId%28%29

nếu tôi nhớ lại trên trình giả lập này trả về 0. tuy nhiên, không có tài liệu nào tôi có thể tìm thấy đảm bảo điều đó. mặc dù trình giả lập có thể không luôn luôn trả về 0, nhưng có vẻ khá an toàn rằng điện thoại đã đăng ký sẽ không trả về 0. điều gì sẽ xảy ra trên thiết bị Android không có điện thoại hoặc không có thẻ SIM được cài đặt hoặc điện thoại hiện chưa được đăng ký trên mạng?

Có vẻ như đó là một ý tưởng tồi, phụ thuộc vào điều đó.

điều đó cũng có nghĩa là bạn cần phải xin phép đọc trạng thái điện thoại, điều này thật tệ nếu bạn chưa yêu cầu nó cho việc khác.

nếu không, thì luôn luôn lật một chút ở đâu đó trước khi cuối cùng bạn tạo ứng dụng đã ký.


5
IMEI có khả năng cũng sẽ quay trở lại 0trên máy tính bảng Android hoặc trên điện thoại không có thẻ SIM.
Paul Lammertsma

Chúng ta có thể chỉnh sửa IMEI trên trình giả lập. vì vậy điều này có thể không phục vụ mục đích. Ngoài ra, bắt đầu từ API 29, chúng tôi không thể truy cập IMEI.
Ananth

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

Điều này sẽ trả về true nếu ứng dụng đang chạy trên trình giả lập.

Điều chúng ta nên cẩn thận là không phát hiện ra tất cả các trình giả lập vì chỉ có một số trình giả lập khác nhau. Nó rất dễ dàng để kiểm tra. Chúng tôi phải đảm bảo rằng các thiết bị thực tế không được phát hiện dưới dạng trình giả lập.

Tôi đã sử dụng ứng dụng có tên " Chia sẻ thông tin thiết bị Android " để kiểm tra điều này.

Trên ứng dụng này, bạn có thể thấy nhiều loại thông tin của nhiều thiết bị (có thể là hầu hết các thiết bị trên thế giới; nếu thiết bị bạn đang sử dụng bị thiếu trong danh sách, nó sẽ được thêm tự động).


Trên Genymotion của tôi chạy trên mac Build.DEVICE = vbox86p
lxknvlk 16/2/2016

3

Trên thực tế, ANDROID_ID trên 2.2 luôn bằng 9774D56D682E549C (theo chủ đề này + thí nghiệm của riêng tôi).

Vì vậy, bạn có thể kiểm tra một cái gì đó như thế này:

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

Không phải là đẹp nhất, nhưng nó làm công việc.


8
Tôi sẽ cẩn thận với điều đó vì lỗi khủng khiếp này: code.google.com/p/android/issues/detail?id=10603
Brandon O'Rourke

3

Điều này làm việc cho tôi

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

3
kỹ sư phần mềm chúng tôi có trong nhà đã không cập nhật điều này; nhận Build.Man sản xuất trên phần cứng của chúng tôi trả về "không xác định". Dấu vân tay có vẻ như là một cách tốt hơn.
Ai đó ở đâu đó

3

Đặt một tệp trong hệ thống tệp của trình giả lập; vì tệp sẽ không tồn tại trên thiết bị thực, nên tệp này sẽ ổn định, đáng tin cậy và dễ sửa khi bị hỏng.


3

Tôi đã thu thập tất cả các câu trả lời cho câu hỏi này và đưa ra chức năng để phát hiện xem Android có chạy trên trình giả lập vm / không:

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;
    }

Đã thử nghiệm trên Trình giả lập, Genymotion và Bluestacks (ngày 1 tháng 10 năm 2015).


3

Kiểm tra câu trả lời, không ai trong số họ làm việc khi sử dụng trình giả lập LeapDroid, Droid4x hoặc Andy,

Những gì không làm việc cho tất cả các trường hợp là như sau:

 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;
}


Andy_46.16_48 trả lại "andy" cho Build.HARDWARE
Doug Voss

Dẫn dương tính giả cho các thiết bị Samsung J series. Được sử dụng sau đây để phát hiện trình giả lập: github.com/gingo/android-emulator-detector
bluetoothfx

2

Vì công cụ mô phỏng cơ bản cho Genymotion là VirtualBox và điều đó sẽ không thay đổi bất cứ lúc nào sớm, tôi đã tìm thấy đoạn mã sau đáng tin cậy nhất:

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

2

Cho dù mã bạn sử dụng để làm phát hiện giả lập, tôi muốn khuyên bạn nên viết bài kiểm tra đơn vị để trang trải tất cả các Build.FINGERPRINT, Build.HARDWAREBuild.MANUFACTURERcác giá trị mà bạn đang phụ thuộc vào. Dưới đây là một số ví dụ kiểm tra:

@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();
}

... Và đây là mã của chúng tôi (nhật ký gỡ lỗi và các bình luận bị xóa vì sự đồng nhất):

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;
    }
}

2

Một tùy chọn khác là kiểm tra xem bạn đang ở chế độ gỡ lỗi hay chế độ sản xuất:

if (BuildConfig.DEBUG) { Log.i(TAG, "I am in debug mode"); }

đơn giản và đáng tin cậy

Không hoàn toàn là câu trả lời của câu hỏi nhưng trong hầu hết các trường hợp, bạn có thể muốn phân biệt giữa phiên gỡ lỗi / phiên kiểm tra và phiên đời của cơ sở người dùng của bạn.

Trong trường hợp của tôi, tôi đặt phân tích google thành DryRun () khi ở chế độ gỡ lỗi để phương pháp này hoạt động hoàn toàn tốt với tôi.


Đối với người dùng cao cấp hơn có một tùy chọn khác. biến thể xây dựng gradle:

trong tệp lớp của ứng dụng của bạn thêm một biến thể mới:

buildTypes {
    release {
        // some already existing commands
    }
    debug {
        // some already existing commands
    }
    // the following is new
    test {
    }
}

Trong mã của bạn, hãy kiểm tra loại bản dựng:

if ("test".equals(BuildConfig.BUILD_TYPE)) { Log.i(TAG, "I am in Test build type"); }
 else if ("debug".equals(BuildConfig.BUILD_TYPE)) { Log.i(TAG, "I am in Debug build type"); }

Bây giờ bạn có cơ hội để xây dựng 3 loại ứng dụng khác nhau.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.