ฉันจะตรวจจับได้อย่างไรเมื่อแอปพลิเคชัน Android ทำงานในตัวจำลอง


313

ฉันต้องการให้โค้ดของฉันทำงานแตกต่างกันเล็กน้อยเมื่อทำงานกับอีมูเลเตอร์มากกว่าเมื่อทำงานบนอุปกรณ์ ( ตัวอย่างเช่นการใช้ 10.0.2.2 แทน URL สาธารณะเพื่อทำงานกับเซิร์ฟเวอร์การพัฒนาโดยอัตโนมัติ) วิธีที่ดีที่สุดในการตรวจสอบคือเมื่อแอปพลิเคชัน Android กำลังทำงานในโปรแกรมจำลอง?


2
android.os.Buildอาจมีลักษณะที่
yanchenko

11
ตะลึงพรึงเพริดฉัน ... Google ควรมีวิธีการมาตรฐานในการทำเช่นนี้?
powder366

@ kreker ปัญหาคืออะไรคุณกำลังเผชิญกับการแก้ปัญหาที่มีอยู่?
เขมราฐ

@Khemraj ปัญหาการทุจริต คนชั่วสามารถเยาะเย้ยเซ็นเซอร์บางอย่างและเปลี่ยนสายบางอย่างเพื่อแกล้งอุปกรณ์จริง
kreker

คำตอบ:


159

วิธีการแก้ปัญหานี้:

    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

โปรดทราบว่าอีมูเลเตอร์บางรุ่นปลอมรายละเอียดที่แน่นอนของอุปกรณ์จริงดังนั้นจึงไม่สามารถตรวจจับได้

ตัวอย่างข้อมูลเล็ก ๆ ที่คุณสามารถทำได้ใน APK เพื่อแสดงสิ่งต่าง ๆ เกี่ยวกับมันเพื่อให้คุณสามารถเพิ่มกฎของคุณเอง:

        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
นั่นคือวิธีที่ Facebook ตรวจพบการเลียนแบบในการตอบสนองพื้นเมือง
Vaiden

นี่คือสิ่งที่ฉันต้องอัปเดตหลังจากใช้คำตอบจาก @Aleadam ค่อนข้างนาน (มันหยุดทำงานสำหรับฉัน)
ckbhodge

@Sid สิ่งที่ควรเพิ่มสำหรับมัน?
นักพัฒนา android

2
@Sid คุณพิมพ์ตัวแปรคลาส Build ที่นั่นหรือไม่? ดูเหมือนไม่มีอะไรพิเศษหรือไม่? คุณได้ลองแล้ว: github.com/framgia/android-emulator-detector ?
นักพัฒนา android

1
@DrDeo คุณสามารถเพิ่มการตรวจสอบบิลด์ปัจจุบันโดยใช้ BuildConfig.DEBUG หรือสร้างบิลด์ของคุณเองด้วยตัวแปรที่คุณกำหนดเอง คุณอาจสามารถใช้ Proguard เพื่อทำให้ฟังก์ชั่นนี้คืนค่าเท็จหรือบางสิ่งบางอย่าง (คุณสามารถลบบันทึกได้เช่นที่แสดงที่นี่: medium.com/tixdo-labs/ ......ดังนั้นอาจเป็นไปได้เช่นกัน)
นักพัฒนา Android

118

คนธรรมดาคนหนึ่งน่าจะเป็น Build.FINGERPRINT.contains("generic")


ใช้งานได้กับ Galaxy Tab Emulator คำตอบที่ชอบไม่ได้
BufferStack

10
โปรดระบุว่าลายนิ้วมือที่มี "generic" นั้นเป็นอีมูเลเตอร์หรืออุปกรณ์ ข้อมูลนั้นเป็นกุญแจสำคัญ แต่ไม่ได้ให้มา
James Cameron

2
Emulator - ตัดสินโดยความคิดเห็นต่อหน้าคุณ :)
Dori

7
สิ่งนี้จะคืนความจริงให้กับอุปกรณ์ของฉันที่ใช้ CyanogenMod ดังนั้นโปรดระวัง
ardevd

8
เอกสาร Androidพูดว่าคุณไม่ควรพยายามที่จะตีความFINGERPRINTคุ้มค่า
gnuf

64

id Android ที่ใช้งานไม่ได้กับฉันตอนนี้ฉันใช้:

"google_sdk".equals( Build.PRODUCT );

35
ใครก็ตามที่อ่านข้อความนี้อาจสนใจที่จะรู้ว่าสตริงนี้ดูเหมือนจะเปลี่ยนเป็น 'sdk' แทนที่จะเป็น 'google_sdk'
Daniel Sloof

15
@Daniel: ฉันใช้ 2.3.3 กับ Google API และมีข้อความว่า 'google_sdk' ดูเหมือนว่าเป็น 'google_sdk' สำหรับ AVD ด้วย Google API และ 'sdk' สำหรับคนปกติ
Randy Sugianto 'Yuku'

3
ตัวเลียนแบบ Intel คืนค่า "full_x86" ดังนั้นฉันจะไม่นับวิธีนี้
462982

3
@GlennMaynard รูปแบบย้อนกลับน่าเกลียด แต่ในทางปฏิบัติ: Build.PRODUCT อาจเป็นโมฆะในขณะที่ "google_sdk" ไม่สามารถทำได้ดังนั้นแบบฟอร์มนี้จะหลีกเลี่ยงข้อผิดพลาดในการอ้างอิง null ที่อาจเกิดขึ้น
Rupert Rawnsley

4
รวมถึงกรณีเพิ่มเติม: "google_sdk" .equals (Build.PRODUCT) || "sdk" .equals (Build.PRODUCT) || "sdk_x86" .equals (Build.PRODUCT) || "vbox86p" .equals (Build.PRODUCT)
Alberto Alonso Ruibal

31

จากคำแนะนำจากคำตอบอื่น ๆ นี่อาจเป็นวิธีที่มีประสิทธิภาพที่สุด:

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


ใช่. ไม่เหมือนกับ Build.PRODUCT, Build.HARDWARE (ปลาทอง) จะเหมือนกันสำหรับ SDK อย่างเป็นทางการและ AOSP ก่อนหน้า API 8 คุณต้องใช้การสะท้อนเพื่อรับที่ฟิลด์ HARDWARE
David Chandler

4
ฉันจะไปกับisEmulator = Build.HARDWARE.contains("golfdish")
ล์มส์

7
@holmes: typo, s / b "ปลาทอง"
โนอาห์

7
สำหรับภาพ Android 5.1 x86_64 (และอาจเป็นรูปภาพ 64 บิตล่าสุดอื่น ๆ ) ที่อาจเป็น "ranchu" แทนที่จะเป็น "ปลาทอง"
warbi

28

Google ใช้รหัสนี้ในปลั๊กอินข้อมูลอุปกรณ์จาก Flutter เพื่อตรวจสอบว่าอุปกรณ์เป็นตัวจำลองหรือไม่:

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

จะมีวิธีเช่นรหัสด้านล่างเพื่อบอกว่าแอปของคุณลงชื่อด้วยรหัสดีบั๊กหรือไม่ มันไม่ได้ตรวจจับตัวจำลอง แต่มันอาจทำงานได้ตามวัตถุประสงค์ของคุณ?

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
ขอบคุณสำหรับรหัสนี้ ฉันได้ตรวจสอบและใช้งานได้แล้วการจัดการคีย์การดีบักแบบยาวอาจเจ็บปวด แต่ทำได้เพียงครั้งเดียวเท่านั้น นี่เป็นทางออกเดียวที่เชื่อถือได้เนื่องจากคำตอบอื่น ๆ ทั้งหมดเปรียบเทียบบางส่วนของสตริงข้อมูลการสร้างระบบปฏิบัติการด้วยสตริงแบบคงที่และสิ่งนี้สามารถและเปลี่ยนได้ในเวอร์ชั่น Android SDK และยังสามารถสร้างขึ้นมาได้โดยการสร้าง Android แบบกำหนดเอง
ZoltanF

ฉันคิดว่ามันเป็นทางออกเดียวที่เชื่อถือได้ อย่างไรก็ตามคีย์ดีบักสามารถเปลี่ยนแปลงได้เร็วกว่าที่เราต้องการ
rds

2
BuildConfig.DEBUGวิธีที่ดีกว่าที่จะทำคือ
Mygod

13

รหัสนี้ใช้ได้สำหรับฉัน

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

ในกรณีที่อุปกรณ์ไม่มีซิมการ์ดมันจะคืนค่าสตริงว่าง: ""

เนื่องจาก Android emulator retuns "Android" เป็นผู้ดำเนินการเครือข่ายฉันจึงใช้โค้ดด้านบน


3
อุปกรณ์ที่ไม่มีซิมการ์ด (เช่นแท็บเล็ต) ส่งคืนอะไร
rds

การรันโปรแกรมจำลองสำหรับ Android 2.1 รหัสนี้ใช้งานได้สำหรับฉัน แต่ตั้งแต่อัพเกรด Cordova เป็น 2.7.0 ตัวแปรบริบทดูเหมือนจะไม่ได้กำหนดหรือบางอย่าง นี่คือข้อผิดพลาดที่ฉันได้รับใน ADT: "บริบทไม่สามารถแก้ไขเป็นตัวแปรได้" นอกจากนี้ตามความคิดเห็นข้างต้นนี่ไม่ใช่วิธีการที่เชื่อถือได้ (แม้ว่าฉันจะไม่ได้ทำเอง)
Rustavore

2
@rds อุปกรณ์ที่ไม่มีซิมการ์ดจะส่งคืนสตริงว่างเปล่า ("")
JJ Kim

ไม่มีวิธีที่จะมีค่านี้ด้วยโปรแกรมจำลองหรือไม่? เพราะฉันต้องการปิดกั้นผู้ใช้ทั้งหมดหากพวกเขาไม่มีซิมการ์ด
c-an

11

ทั้งสองอย่างต่อไปนี้ถูกตั้งค่าเป็น "google_sdk":

Build.PRODUCT
Build.MODEL

ดังนั้นควรจะเพียงพอที่จะใช้หนึ่งในบรรทัดต่อไปนี้

"google_sdk".equals(Build.MODEL)

หรือ

"google_sdk".equals(Build.PRODUCT)

เมื่อใช้จำลอง x86 บน Windows, Build.Product sdk_x86คือ
Edward Brey

การตรวจสอบกับ PRODUCT ไม่ใช่ทางเลือกที่ดีเนื่องจากส่งคืนค่าต่าง ๆ จากอีมูเลเตอร์ต่าง ๆ
Beeing Jk

11

ฉันลองใช้เทคนิคหลายอย่าง แต่ตัดสินในรุ่นที่ปรับปรุงเล็กน้อยของการตรวจสอบ Build.PRODUCT ดังนี้ ดูเหมือนว่าจะแตกต่างกันเล็กน้อยจากตัวเลียนแบบถึงตัวเลียนแบบนั่นคือเหตุผลที่ฉันมี 3 เช็คที่ฉันมีอยู่ในปัจจุบัน ฉันคิดว่าฉันสามารถตรวจสอบได้เพียงแค่ว่า product.contain ("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;
}

FYI - ฉันพบว่า Kindle Fire ของฉันมี BuildBRAND = "generic" และอีมูเลเตอร์บางตัวไม่มี "Android" สำหรับผู้ให้บริการเครือข่าย


10

ฉันเพียงแค่มองหา_sdk, _sdk_หรือsdk_หรือแม้เพียงแค่sdkมีส่วนร่วมในBuild.PRODUCT:

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

3
ทำไมไม่เพียงcontains("sdk")? ความแตกต่างเพียงอย่างเดียว (นอกเหนือจากการเร็วกว่า) คือmatches(".*_?sdk_?.*")ต้องการให้มีหากมีตัวละครก่อนหรือหลัง sdk จะต้องมีขีดล่าง '_' ซึ่งไม่ใช่ทั้งหมดที่สำคัญในการตรวจสอบ
Nulano

9

ฉันไม่เคยพบวิธีที่ดีที่จะบอกว่าคุณอยู่ในโปรแกรมจำลอง

แต่ถ้าคุณเพียงต้องการ detecet ถ้าคุณอยู่ในสภาพแวดล้อมการพัฒนาคุณสามารถทำได้:

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

หวังว่าความช่วยเหลือนี้ ....


8

ใช้ฟังก์ชั่นนี้:

 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

ไม่ทราบว่ามีวิธีที่ดีกว่าในการตรวจจับอีมูหรือไม่ แต่อีมูเลเตอร์จะมีไฟล์ init.goldfish.rcในรูทไดเร็กทอรี

มันเป็นสคริปต์เริ่มทำงานเฉพาะของอีมูเลเตอร์และมันไม่ควรจะอยู่ในโครงสร้างบิลด์ของอีมูเลเตอร์


ในระหว่างการเริ่มต้นระบบ Android เคอร์เนล Linux จะเรียกกระบวนการ "init" ก่อน init อ่านไฟล์ "/init.rc" และ "init.device.rc" "init.device.rc" เป็นอุปกรณ์เฉพาะบนอุปกรณ์เสมือนไฟล์นี้เรียกว่า "init.goldfish.rc"
NET3

7

นี่คือโซลูชันของฉัน (ใช้ได้เฉพาะเมื่อคุณเรียกใช้เว็บเซิร์ฟเวอร์บนเครื่องดีบั๊กของคุณ): ฉันได้สร้างงานพื้นหลังที่เริ่มต้นเมื่อแอปพลิเคชันเริ่มทำงาน จะค้นหาhttp://10.0.2.2และหากมีอยู่จะเปลี่ยนพารามิเตอร์ส่วนกลาง (IsDebug) เป็นจริง มันเป็นวิธีที่เงียบในการค้นหาตำแหน่งที่คุณกำลังทำงาน

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

จากกิจกรรมหลักบนสร้าง:

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

7

จากแบตเตอรี่ตัวจำลอง: แหล่งจ่ายไฟเป็นอุปกรณ์ชาร์จ AC เสมอ อุณหภูมิอยู่เสมอ 0

และคุณสามารถใช้Build.HOSTเพื่อบันทึกค่าโฮสต์อีมูเลเตอร์ต่าง ๆ มีค่าโฮสต์ที่แตกต่างกัน


คุณจะได้แหล่งพลังงานและอุณหภูมิได้อย่างไร
นักพัฒนา android

5

อีกทางเลือกหนึ่งคือดูที่คุณสมบัติ ro.hardware และดูว่าชุดนั้นเป็นปลาทองหรือไม่ แต่น่าเสียดายที่ไม่ได้ดูเหมือนจะเป็นวิธีที่ง่ายที่จะทำนี้จาก Java แต่เล็กน้อยจาก C ใช้property_get ()


4
ดูเหมือนว่าจะทำงานจาก NDK รวม <sys / system_properties.h> และใช้ __system_property_get ("ro.hardware", buf) จากนั้นตรวจสอบว่า buf คือ "ปลาทอง"
NuSkooler

5

วิธีการแก้ปัญหาที่แนะนำข้างต้นเพื่อตรวจสอบ ANDROID_IDทำงานสำหรับฉันจนกว่าฉันจะอัปเดตในวันนี้เป็นเครื่องมือ SDK ล่าสุดที่วางจำหน่ายพร้อมกับ Android 2.2

ดังนั้นในปัจจุบันฉันเปลี่ยนไปใช้วิธีแก้ปัญหาต่อไปนี้ซึ่งใช้งานได้ดีกับข้อเสียอย่างไรก็ตามคุณต้องใส่สิทธิ์อนุญาตการอ่าน 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

คำตอบทั้งหมดในวิธีเดียว

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

ทำได้ดีนี่. init.goldfish.rcมีอยู่ในอีมูเลเตอร์เท่านั้น นอกจากนี้ยังเป็นการตรวจสอบที่ดีในอนาคตนอกเหนือจากรายละเอียดการสร้าง
sud007

2
@ sud007 มีอุปกรณ์มากมายออกมาพร้อมกับ `/init.goldfish.rc และสิ่งนี้จะนำไปสู่ผลบวกปลอม ตัวอย่างเช่นอุปกรณ์ Samsung Galaxy หลายรุ่น
laalto

@alalto คุณถูกต้องจริง ฉันพบว่าในภายหลังและขอโทษที่ฉันลืมอัปเดตที่นี่
sud007

ปุ่มทดสอบได้สร้างผลบวกปลอมสำหรับฉันแล้ว
Avi Parshan

อุปกรณ์ใดที่พวกเขากำลังสร้างผลบวกปลอม?
Aman Verma

5

Build.HARDWARE = "ranchu"ผมพบว่าจำลองใหม่

การอ้างอิง: https://groups.google.com/forum/#!topic/android-emulator-dev/dltBnUW_HzU

และฉันก็พบวิธีที่เป็นทางการของ Android เพื่อตรวจสอบว่าอีมูเลเตอร์หรือไม่ฉันคิดว่ามันเป็นข้อมูลอ้างอิงที่ดีสำหรับเรา

ตั้งแต่ Android API ระดับ 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 ระดับ 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ตรวจสอบว่าอีมูเลเตอร์

วิธีที่เจ้าหน้าที่ตรวจสอบว่าอีมูเลเตอร์ไม่ใช่ของใหม่และอาจไม่พอคำตอบข้างต้นก็กล่าวถึงเช่นกัน

แต่นี่อาจแสดงให้เราเห็นว่าเจ้าหน้าที่จะให้วิธีการอย่างเป็นทางการเพื่อตรวจสอบว่าโปรแกรมจำลองหรือไม่

ดังที่ได้กล่าวมาแล้วข้างต้นตอนนี้เราสามารถใช้สองวิธีในการตรวจสอบว่าอีมูเลเตอร์

วิธีเข้าถึงcom.android.internalแพ็คเกจและ@hide

และรอ SDK เปิดอย่างเป็นทางการ


5

คำแนะนำของฉัน:

ลองสิ่งนี้จาก GitHub

ง่ายต่อการตรวจสอบโปรแกรมจำลอง android

  • ตรวจสอบอุปกรณ์จริงใน Device Farm ( https://aws.amazon.com/device-farm/ )
  • BlueStacks
  • Genymotion
  • Android Emulator
  • แอนดี้ 46.2.207.0
  • ฉันเล่น
  • Nox App Player
  • Koplayer
  • .....

วิธีใช้กับตัวอย่าง:

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

                    }
                });

4

คุณสามารถตรวจสอบ IMEI # http://developer.android.com/reference/android/telephony/TelephonyManager.html#getDeviceId%28%29

ถ้าฉันจำได้ว่าอิมูเลเตอร์คืนนี้เป็น 0 แต่ไม่มีเอกสารที่ฉันสามารถพบได้ว่ารับประกันได้ แม้ว่าอีมูเลเตอร์อาจไม่ส่งคืนค่า 0 เสมอ แต่มันค่อนข้างปลอดภัยที่โทรศัพท์ที่ลงทะเบียนจะไม่ส่งคืน 0 สิ่งที่จะเกิดขึ้นบนอุปกรณ์ android ที่ไม่ใช่โทรศัพท์หรือรุ่นที่ไม่มีซิมการ์ดติดตั้งหรือที่ไม่ได้ลงทะเบียนในปัจจุบัน เครือข่าย?

ดูเหมือนว่าจะเป็นความคิดที่ไม่ดีขึ้นอยู่กับว่า

นอกจากนี้ยังหมายความว่าคุณต้องขออนุญาตเพื่ออ่านสถานะโทรศัพท์ซึ่งไม่ดีหากคุณไม่ได้ต้องการสิ่งอื่น

หากไม่ใช่อย่างนั้นจะมีการพลิกบางส่วนอยู่เสมอก่อนที่คุณจะสร้างแอปที่ลงชื่อของคุณ


5
IMEI มีแนวโน้มที่จะส่งคืน0บนแท็บเล็ต Android หรือบนโทรศัพท์ที่ไม่มีซิมการ์ด
Paul Lammertsma

เราสามารถแก้ไข IMEI บนตัวจำลอง ดังนั้นสิ่งนี้อาจไม่เป็นไปตามวัตถุประสงค์ นอกจากนี้เริ่มจาก API 29 เราไม่สามารถเข้าถึง IMEI ได้
Ananth

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

สิ่งนี้จะกลับมาเป็นจริงหากแอปกำลังทำงานกับตัวจำลอง

สิ่งที่เราควรระวังไม่ได้ตรวจจับตัวเลียนแบบทั้งหมดเพราะมีตัวเลียนแบบหลายตัวเท่านั้น ง่ายต่อการตรวจสอบ เราต้องตรวจสอบให้แน่ใจว่าอุปกรณ์จริงไม่ได้ถูกตรวจจับเป็นตัวจำลอง

ฉันใช้แอพชื่อว่า " แบ่งปันข้อมูลอุปกรณ์ Android " เพื่อตรวจสอบสิ่งนี้

ในแอพนี้คุณสามารถดูข้อมูลประเภทต่าง ๆ ของอุปกรณ์จำนวนมาก (อาจเป็นอุปกรณ์ส่วนใหญ่ในโลกถ้าอุปกรณ์ที่คุณใช้หายไปจากรายการมันจะถูกเพิ่มโดยอัตโนมัติ)


ใน Genymotion ของฉันทำงานบน mac Build.DEVICE = vbox86p
lxknvlk

3

อันที่จริงแล้ว ANDROID_ID ใน 2.2 จะเท่ากับ9774D56D682E549Cเสมอ(ตามหัวข้อนี้ + การทดลองของฉันเอง)

ดังนั้นคุณสามารถตรวจสอบสิ่งนี้:

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

ไม่ใช่คนที่สวยที่สุด แต่ก็ทำงานได้ดี


8
ฉันจะต้องระมัดระวังด้วยเพราะข้อผิดพลาดที่น่ากลัวนี้: code.google.com/p/android/issues/detail?id=10603
Brandon O'Rourke

3

มันใช้งานได้สำหรับฉัน

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

3
วิศวกรเฟิร์มแวร์ที่เรามีอยู่ในบ้านไม่ได้อัปเดต รับ Build.Manufacturer บนฮาร์ดแวร์ของเรากลับ "ไม่ทราบ" ลายนิ้วมือดูเหมือนจะเป็นวิธีที่ดีกว่า
คนที่ไหนสักแห่งที่

3

วางไฟล์ในระบบไฟล์ของตัวจำลอง เนื่องจากไฟล์จะไม่มีอยู่ในอุปกรณ์จริงจึงควรมีเสถียรภาพเชื่อถือได้และง่ายต่อการแก้ไขเมื่อแบ่งไฟล์


3

ฉันได้รวบรวมคำตอบทั้งหมดสำหรับคำถามนี้แล้วและมีฟังก์ชันเพื่อตรวจสอบว่า Android ทำงานบน vm / emulator หรือไม่:

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

ทดสอบกับ Emulator, Genymotion และ Bluestacks (1 ตุลาคม 2558)


3

การตรวจสอบคำตอบไม่มีใครทำงานได้เมื่อใช้ LeapDroid, Droid4x หรือ Andy emulators

สิ่งที่ใช้ได้กับทุกกรณีมีดังต่อไปนี้:

 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 ส่งคืน "andy" สำหรับ Build.HARDWARE
Doug Voss

นำไปสู่การบวกเท็จสำหรับอุปกรณ์ Samsung J series ใช้สิ่งต่อไปนี้เพื่อตรวจจับโปรแกรมจำลอง: github.com/gingo/android-emulator-detector
bluetoothfx

2

เนื่องจากเอ็นจิ้นการจำลองพื้นฐานสำหรับ Genymotion คือ VirtualBox และนั่นจะไม่เปลี่ยนแปลงตลอดเวลาในไม่ช้าฉันจึงพบว่ารหัสต่อไปนี้น่าเชื่อถือที่สุด:

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

2

ใดก็ตามที่คุณใช้รหัสที่จะทำการตรวจสอบจำลองผมขออยากแนะนำให้เขียนทดสอบหน่วยเพื่อให้ครอบคลุมทั้งหมดBuild.FINGERPRINT, Build.HARDWAREและBuild.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;
    }
}

2

ตัวเลือกอื่นคือตรวจสอบว่าคุณอยู่ในโหมดดีบักหรือโหมดการผลิต:

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

ง่ายและเชื่อถือได้

ไม่ใช่คำตอบของคำถามทั้งหมด แต่ในกรณีส่วนใหญ่คุณอาจต้องการแยกแยะระหว่างเซสชันการดีบัก / ทดสอบและช่วงชีวิตของฐานผู้ใช้ของคุณ

ในกรณีของฉันฉันตั้งค่า Google Analytics เป็น dryRun () เมื่ออยู่ในโหมดแก้ไขข้อบกพร่องดังนั้นวิธีนี้จึงใช้ได้ดีสำหรับฉัน


สำหรับผู้ใช้ขั้นสูงเพิ่มเติมมีตัวเลือกอื่น สายพันธุ์สร้าง gradle:

ในไฟล์ gradle ของแอปเพิ่มตัวแปรใหม่:

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

ในรหัสของคุณตรวจสอบประเภทการสร้าง:

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

ตอนนี้คุณมีโอกาสที่จะสร้างแอพของคุณ 3 ประเภท

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.