بدء استخدام اختبارات Game Loop

قد يكون من الصعب إعداد اختبارات الألعاب تلقائيًا عندما يتم إنشاء تطبيقات الألعاب على أُطر واجهة مستخدم مختلفة. تتيح لك اختبارات حلقة الألعاب دمج اختباراتك الأصلية مع Test Lab وإجرائها بسهولة على الأجهزة التي تختارها. يُجري اختبار "حلقة الألعاب" اختبارك من خلال لعبة على الأجهزة الجوّالة مع محاكاة إجراءات لاعب حقيقي. يوضّح لك هذا الدليل كيفية إجراء اختبار "حلقة الألعاب"، ثم عرض نتائج الاختبار وإدارتها في وحدة تحكّم Firebase.

ما هو اختبار حلقة الألعاب؟

بما أنّ تطبيقات الألعاب يتم عرضها على مستوى أُطر عمل ومحرّكات مختلفة لواجهة المستخدم، مثل Unity أو Unreal أو رمز OpenGL/Vulkan C++‎، لا يمكن لأدوات التشغيل الآلي العادية فحص عناصر واجهة المستخدم أو التفاعل معها. يتيح لك اختبار حلقة اللعبة دمج اختبارات المحرّك الأصلية مع Test Lab من خلال تشغيل لعبتك باستخدام هدف يتضمّن رقم سيناريو محدّدًا. يدفع رقم السيناريو هذا تطبيقك إلى تنفيذ منطق مكتوب مسبقًا أو عمليات محاكاة مستندة إلى الذكاء الاصطناعي أو عمليات تحقّق من الأداء مباشرةً في محرك الألعاب.

بعد تشغيل Test Lab، لا يتفاعل مع واجهة مستخدم لعبتك. بدلاً من ذلك، تعمل Test Lab كبيئة نقل واختبار: فهي تشغّل السيناريو المحدّد، وتتتبّع الأعطال ومهلات الاستجابة، وتجمع أي سجلّات نتائج مخصّصة عند انتهاء تطبيقك من تنفيذ الحلقة.

أوجه الاختلاف بين حلقات الألعاب واختبارات الأجهزة

  • تعتمد اختبارات قياس حالة التطبيق (مثل Espresso أو UI Automator) على أُطر عمل خارجية لتنفيذ تفاعلات واجهة المستخدم والنقر على الأزرار وتأكيد حالة التطبيق على مستوى هيكلية طرق العرض لعناصر Android View من خارج تطبيقك.
  • تفوِّض اختبارات حلقات الألعاب منطق الاختبار إلى تطبيقك، إذ ينفِّذ تطبيقك حلقة اختبار داخلية أو محاكاة بدون تفاعل خارجي مع واجهة المستخدم، بينما يفعِّل Test Lab السيناريو ويجمع نتائج الاختبار.

أمثلة

لنفترض أنّك تريد التأكّد من أنّ لعبتك تحافظ على 60 إطارًا في الثانية على الأقل على أجهزة فعلية مختلفة. يمكنك تنفيذ السيناريو 1 داخل رمز اللعبة لتحميل المستوى الأكثر تعقيدًا من الناحية المرئية والسماح للاعب من الذكاء الاصطناعي أو لبرنامج نصي آلي بتشغيله لمدة دقيقتَين.

عندما يؤدي Test Lab إلى تشغيل السيناريو 1، تبدأ لعبتك في تنفيذ تجربة اللعب باستخدام الذكاء الاصطناعي، وتتتبّع أوقات عرض اللقطات واستخدام الذاكرة، وتنقل هذه المقاييس إلى ملف سجلّ مخصّص للناتج (logFile)، وتطلب finish() عند اكتمال العملية. بعد ذلك، تلتقط Test Lab ملف السجلّ وتتيحه لك في Cloud Storage وفي وحدة تحكّم Firebase.

حالات الاستخدام

ويمكنك تنفيذ الاختبارات باستخدام حلقة واحدة أو حلقات متعددة، وذلك حسب محرك الألعاب الذي تستخدمه. الحلقة هي عملية تشغيل كاملة أو جزئية للاختبار على لعبة على الأجهزة الجوّالة. يمكن استخدام حلقات الألعاب في ما يلي:

  • تشغيل مستوى من لعبتك بالطريقة نفسها التي يلعب بها المستخدم النهائي يمكنك إما كتابة نص برمجي لإدخال المستخدم أو السماح للمستخدم بعدم اتخاذ أي إجراء أو استبدال المستخدم بذكاء اصطناعي إذا كان ذلك منطقيًا في لعبتك (على سبيل المثال، إذا كان لديك تطبيق ألعاب لسباق السيارات وسبق لك تنفيذ ذكاء اصطناعي، يمكنك وضع سائق ذكاء اصطناعي مسؤولاً عن إدخال المستخدم).
  • شغِّل لعبتك بأعلى إعدادات الجودة لمعرفة ما إذا كانت الأجهزة متوافقة معها.
  • إجراء اختبار فني (تجميع برامج تظليل متعددة وتنفيذها والتحقّق من أنّ الناتج هو كما هو متوقّع وما إلى ذلك)

يمكنك إجراء اختبار "حلقة الألعاب" على جهاز اختبار واحد أو مجموعة من أجهزة الاختبار أو على Test Lab. ومع ذلك، لا ننصح بإجراء اختبارات "حلقة الألعاب" على الأجهزة الافتراضية لأنّ معدلات عرض الرسومات فيها أقل من الأجهزة الفعلية.

قبل البدء

لتنفيذ اختبار، عليك أولاً إعداد تطبيقك لاختبارات "حلقات الألعاب".

  1. في بيان تطبيقك، أضِف intent filter جديدًا إلى نشاطك:

    <activity android:name=".MyActivity">
       <intent-filter>
           <action android:name="com.google.intent.action.TEST_LOOP"/>
           <category android:name="android.intent.category.DEFAULT"/>
           <data android:mimeType="application/javascript"/>
       </intent-filter>
       <intent-filter>
          ... (other intent filters here)
       </intent-filter>
    </activity>

    يتيح ذلك لـ Test Lab تشغيل لعبتك من خلال تفعيلها باستخدام غرض محدّد.

  2. في الرمز البرمجي (ننصحك بإضافته داخل تعريف طريقة onCreate)، أضِف ما يلي:

    Kotlin

    val launchIntent = intent
    if (launchIntent.action == "com.google.intent.action.TEST_LOOP") {
        val scenario = launchIntent.getIntExtra("scenario", 0)
        // Code to handle your game loop here
    }

    Java

    Intent launchIntent = getIntent();
    if(launchIntent.getAction().equals("com.google.intent.action.TEST_LOOP")) {
        int scenario = launchIntent.getIntExtra("scenario", 0);
        // Code to handle your game loop here
    }

    يسمح ذلك لنشاطك بالتحقّق من الغرض الذي يتم تشغيله من خلاله. يمكنك أيضًا إضافة هذا الرمز لاحقًا إذا كنت تفضّل ذلك (مثلاً، بعد تحميل محرك الألعاب في البداية).

  3. يُنصح بما يلي: في نهاية الاختبار، أضِف:

    Kotlin

    yourActivity.finish()

    Java

    yourActivity.finish();

    يؤدي ذلك إلى إغلاق تطبيقك عند اكتمال اختبار "حلقة الألعاب". يعتمد الاختبار على إطار عمل واجهة المستخدم لتطبيقك من أجل بدء التكرار التالي، ويؤدي إغلاق تطبيقك إلى إعلام إطار العمل بأنّ الاختبار قد انتهى.

إنشاء اختبار "حلقة الألعاب" وتشغيله

بعد ضبط تطبيقك لإجراء اختبارات "حلقة الألعاب"، يمكنك إنشاء اختبار وتشغيله على الفور في لعبة على الأجهزة الجوّالة. ويمكنك اختيار إجراء اختبار في Test Lab باستخدام وحدة تحكّم Firebase أو واجهة سطر الأوامر (CLI) في gcloud، أو على جهاز محلي باستخدام "اختبار مدير الحلقة".

التشغيل على جهاز محلي

Test LabTest Loop Manager هو تطبيق مفتوح المصدر يساعدك في دمج اختبارات "حلقة الألعاب" وتشغيلها على أجهزتك المحلية. ويتيح أيضًا لفريق ضمان الجودة تشغيل حلقات اللعبة نفسها على أجهزتهم.

لتشغيل اختبار على جهاز محلي باستخدام "أداة إدارة حلقات الاختبار"، اتّبِع الخطوات التالية:

  1. نزِّل Test Loop Manager على هاتف أو جهاز لوحي وثبِّته من خلال تنفيذ ما يلي:
    adb install testloopmanager.apk
  2. على جهازك، افتح تطبيق Test Loop Apps على هاتفك أو جهازك اللوحي. يعرض التطبيق قائمة بالتطبيقات على جهازك التي يمكن تشغيلها باستخدام حلقات الألعاب. إذا لم يظهر تطبيق الألعاب هنا، تأكَّد من أنّ فلتر الغرض يتطابق مع الفلتر الموضّح في الخطوة الأولى من قسم "قبل البدء".
  3. اختَر لعبة على الأجهزة الجوّالة، ثم اختَر عدد مرات التكرار التي تريد تنفيذها. ملاحظة: في هذه الخطوة، يمكنك اختيار تشغيل مجموعة فرعية من الحلقات بدلاً من حلقة واحدة فقط. لمزيد من المعلومات حول تشغيل عدة حلقات في الوقت نفسه، يُرجى الاطّلاع على الميزات الاختيارية.
  4. انقر على تنفيذ الاختبار. يبدأ تشغيل اختبارك على الفور.

التشغيل في Test Lab

يمكنك إجراء اختبار "حلقة الألعاب" في Test Lab باستخدام Firebase Console أو gcloud CLI. قبل البدء، افتح وحدة تحكّم Firebase وأنشئ مشروعًا، إذا لم يسبق لك ذلك.

استخدام وحدة تحكّم Firebase

  1. في وحدة تحكّم Firebase، انقر على Test Lab من اللوحة اليمنى.
  2. انقر على إجراء اختبارك الأول (أو إجراء اختبار إذا سبق لمشروعك إجراء اختبار).
  3. اختَر حلقة الألعاب كنوع الاختبار، ثمّ انقر على متابعة.
  4. انقر على استعراض، ثم استعرض ملف .apk الخاص بتطبيقك. ملاحظة: في هذه الخطوة، يمكنك اختيار تشغيل مجموعة فرعية من الحلقات بدلاً من حلقة واحدة فقط. لمزيد من المعلومات حول تشغيل حلقات متعددة في الوقت نفسه، يمكنك الاطّلاع على الميزات الاختيارية.
  5. انقر على متابعة.
  6. اختَر الأجهزة الفعلية التي تريد استخدامها لاختبار تطبيقك.
  7. انقر على بدء الاختبارات.

لمزيد من المعلومات حول كيفية بدء استخدام وحدة تحكّم Firebase، يُرجى الاطّلاع على مقالة بدء الاختبار باستخدام وحدة تحكّم Firebase.

استخدام سطر الأوامر (CLI) في gcloud

  1. إذا لم يسبق لك إجراء ذلك، نزِّل Google Cloud SDK وثبِّته.

  2. سجِّل الدخول إلى gcloud CLI باستخدام حساب Google:

    gcloud auth login

  3. اضبط مشروعك على Firebase في gcloud، حيث يمثّل PROJECT_ID رقم تعريف مشروعك على Firebase:

    gcloud config set project PROJECT_ID
    
  4. إجراء الاختبار الأول:

    gcloud firebase test android run \
     --type=game-loop --app=<var>path-to-apk</var> \
     --device model=herolte,version=23
    

لمزيد من المعلومات حول كيفية بدء استخدام gcloud CLI، يُرجى الاطّلاع على بدء الاختبار من سطر أوامر gcloud.

الميزات الاختيارية

توفّر Test Lab العديد من الميزات الاختيارية التي تتيح لك تخصيص اختباراتك بشكل أكبر، بما في ذلك إمكانية كتابة بيانات الإخراج، وتوفير الدعم لحلقات لعب متعددة، وتصنيفات للحلقات ذات الصلة.

كتابة بيانات الناتج

عندما يطلق Test Lab لعبتك، يحدّد عنوان URL يشير إلى ملف نتائج محلي على جهاز الاختبار، ويمكن الوصول إليه باستخدام launchIntent.getData(). يمكن أن تسجّل لعبتك نتائج الاختبار ومقاييس الأداء والسجلات (logFile) مباشرةً في هذا الموقع الجغرافي أثناء تشغيل الاختبار.

يمكنك كتابة أي محتوى عشوائي في logFile، مثل نص عادي أو بيانات CSV أو أوقات عرض اللقطات أو تقارير التشخيص المخصّصة. لا توجد قيود أو متطلبات تنسيق للمحتوى الذي تكتبه، بل يتم التقاط الملف عند انتهاء الاختبار وتحميله إلى حزمة Cloud Storage مع عناصر الاختبار الأخرى.

تتّبع Test Lab أفضل الممارسات المعمول بها لمشاركة ملف بين التطبيقات، كما هو موضّح في مشاركة ملف. في طريقة onCreate() الخاصة بنشاطك، حيث يقع الغرض، يمكنك استرداد ملف إخراج البيانات والتحقّق منه من خلال تشغيل الرمز التالي:

Kotlin

val launchIntent = intent
val logFile = launchIntent.data
logFile?.let {
    Log.i(TAG, "Log file ${it.encodedPath}")
    // ...
}

Java

Intent launchIntent = getIntent();
Uri logFile = launchIntent.getData();
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.getEncodedPath());
    // ...
}

إذا أردت الكتابة في الملف من جهة C++ في تطبيق لعبتك، يمكنك تمرير واصف الملف بدلاً من مسار الملف:

Kotlin

val launchIntent = intent
val logFile = launchIntent.data
var fd = -1
logFile?.let {
    Log.i(TAG, "Log file ${it.encodedPath}")
    fd = try {
        contentResolver
            .openAssetFileDescriptor(logFile, "w")!!
            .parcelFileDescriptor
            .fd
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
        -1
    } catch (e: NullPointerException) {
        e.printStackTrace()
        -1
    }
}

// C++ code invoked here.
// native_function(fd);

Java

Intent launchIntent = getIntent();
Uri logFile = launchIntent.getData();
int fd = -1;
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.getEncodedPath());
    try {
        fd = getContentResolver()
                .openAssetFileDescriptor(logFile, "w")
                .getParcelFileDescriptor()
                .getFd();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        fd = -1;
    } catch (NullPointerException e) {
        e.printStackTrace();
        fd = -1;
    }
}

// C++ code invoked here.
// native_function(fd);

C++‎

#include <unistd.h>
JNIEXPORT void JNICALL
Java_my_package_name_MyActivity_native_function(JNIEnv *env, jclass type, jint log_file_descriptor) {
// The file descriptor needs to be duplicated.
int my_file_descriptor = dup(log_file_descriptor);
}

مثال على ملف الإخراج

على الرغم من أنّ Test Lab لا يتطلّب تنسيقًا معيّنًا لـ logFile، فإنّ تنظيم الناتج بتنسيق JSON مطابق للمخطط أدناه يتيح لوحدة تحكّم Firebase (https://console.firebase.google.com/project/_/testlab) تحليل وعرض مقاييس الملخّص لاختبار حلقة اللعبة.

التفاصيل الأساسية حول هذا المثال:

  • مَن يقدّم البيانات: أنت (مطوّر الاختبار) المسؤول عن احتساب جميع المقاييس وكتابتها في هذا الملف من رمز لعبتك. لا يجمع Test Lab نقاط البيانات هذه أو ينشئها لك تلقائيًا.
  • إضافة حقول مخصّصة: تشير المناطق التي تحمل العلامة /.../ إلى الأماكن التي يمكنك فيها إضافة أزواج مفتاح-قيمة مخصّصة عشوائية (على سبيل المثال، "rendered_vertices": 12000 أو "ai_error_count": 0). يمكنك إضافة أي سمة JSON صالحة إلى الناتج عند كتابة الملف من تطبيقك، طالما أنّ أسماء الحقول لا تتعارض مع حقول المخطط، مثل name أو start_timestamp.
  • مكان العثور على ملف الإخراج: بعد اكتمال الاختبار، يتم تخزين ملف الإخراج الأوّلي ويمكن تنزيله من حزمة نتائج Cloud Storage (GCS) الخاصة بمشروعك داخل دليل عناصر اختبار التشغيل. يمكنك أيضًا عرض الناتج الذي تم تحليله وفحصه في صفحة Test Lab ضمن وحدة تحكّم Firebase.

في ما يلي مثال على ملف إخراج JSON منظَّم:

{
  "name": "test name",
  "start_timestamp": 0, // Timestamp of the test start (in us).
                           Can be absolute or relative
  "driver_info": "...",
  "frame_stats": [
    {
      "timestamp": 1200000, // Timestamp at which this section was written
                               It contains value regarding the period
                               start_timestamp(0) -> this timestamp (1200000 us)
      "avg_frame_time": 15320, // Average time to render a frame in ns
      "nb_swap": 52, // Number of frame rendered
      "threads": [
        {
          "name": "physics",
          "Avg_time": 8030 // Average time spent in this thread per frame in us
        },
        {
          "name": "AI",
          "Avg_time": 2030 // Average time spent in this thread per frame in us
        }
      ],
      /.../ // Any custom field you want (vertices display on the screen, nb units …)
    },
    {
      // Next frame data here, same format as above
    }
  ],
  "loading_stats": [
    {
      "name": "assets_level_1",
      "total_time": 7850, // in us
      /.../
    },
    {
      "name": "victory_screen",
      "total_time": 554, // in us
      /.../
    }

  ],
  /.../, // You can add custom fields here
}

حلقات لعب متعدّدة

قد يكون من المفيد تشغيل حلقات ألعاب متعددة في تطبيقك، لأنّ الحلقة هي عملية تشغيل كاملة لتطبيق اللعبة من البداية إلى النهاية. على سبيل المثال، إذا كانت لعبتك تتضمّن مستويات متعدّدة، قد تحتاج إلى حلقة ألعاب واحدة لتشغيل كل مستوى بدلاً من حلقة واحدة تتكرّر خلال جميع المستويات. بهذه الطريقة، إذا تعطّل تطبيقك في المستوى 32، يمكنك تشغيل حلقة اللعبة هذه مباشرةً لإعادة إنتاج العطل واختبار إصلاحات الأخطاء.

للسماح لتطبيقك بتشغيل عدة حلقات في الوقت نفسه، اتّبِع الخطوات التالية:

  • إذا كنت تجري اختبارًا باستخدام "أداة إدارة حلقات الاختبار":

    1. أضِف السطر التالي إلى بيان تطبيقك، داخل العنصر <application>:

      <meta-data
        android:name="com.google.test.loops"
        android:value="5" />

      يحتوي هدف التشغيل هذا على حلقة الاستهداف كمَعلمة عدد صحيح. في الحقل android:value، يمكنك تحديد عدد صحيح من 1 إلى 1024 (الحد الأقصى لعدد التكرارات المسموح به لاختبار واحد). يُرجى العِلم أنّ الفواصل الزمنية يتم فهرسها بدءًا من 1، وليس 0.

    2. في تطبيق "أداة إدارة حلقات الاختبار"، ستظهر شاشة اختيار تتيح لك تحديد الحلقات التي تريد تشغيلها. إذا اخترت حلقات متعدّدة، سيتم تشغيل كل حلقة بالتسلسل بعد اكتمال الحلقة السابقة.

  • إذا كنت تجري اختبارًا باستخدام وحدة تحكّم Firebase، أدخِل قائمة أو نطاقًا من أرقام التكرار في حقل السيناريوهات.

  • إذا كنت تجري اختبارًا باستخدام gcloud CLI، حدِّد قائمة بأرقام التكرار باستخدام العلامة --scenario-numbers. على سبيل المثال، يشغّل --scenario-numbers=1,3,5 الحلقات 1 و3 و5.

  • إذا كنت تكتب رمز C++ وأردت تغيير سلوك الحلقة، مرِّر الإضافة التالية إلى رمز C++ الأصلي:

    Kotlin

    val launchIntent = intent
    val scenario = launchIntent.getIntExtra("scenario", 0)

    Java

    Intent launchIntent = getIntent();
    int scenario = launchIntent.getIntExtra("scenario", 0);

    يمكنك الآن تغيير سلوك الحلقة استنادًا إلى int القيمة الناتجة.

تصنيف حلقات الألعاب

عند تصنيف حلقات الألعاب باستخدام تصنيف واحد أو أكثر من تصنيفات السيناريوهات، يمكنك أنت وفريق ضمان الجودة إطلاق مجموعة من حلقات الألعاب ذات الصلة بسهولة (مثل "جميع حلقات الألعاب المتوافقة") واختبارها في مصفوفة واحدة. يمكنك إنشاء تصنيفاتك الخاصة أو استخدام التصنيفات المحدّدة مسبقًا التي يقدّمها Test Lab:

  • ‫com.google.test.loops.player_experience: حلقات التكرار المستخدَمة لمحاكاة تجربة المستخدم الحقيقي عند تشغيل اللعبة والهدف من الاختبار باستخدام هذه الحلقات هو رصد المشاكل التي قد يواجهها المستخدم الحقيقي أثناء لعب اللعبة.
  • com.google.test.loops.gpu_compatibility: حلقات التكرار المستخدَمة لاختبار المشاكل المتعلّقة بوحدة معالجة الرسومات والهدف من الاختبار باستخدام هذه الحلقات هو تنفيذ رمز وحدة معالجة الرسومات الذي قد لا يعمل بشكل صحيح في الإصدار العلني، وذلك للكشف عن المشاكل المتعلقة بالأجهزة وبرامج التشغيل.
  • ‫com.google.test.loops.compatibility: حلقات for المستخدَمة لاختبار مجموعة كبيرة من مشاكل التوافق، بما في ذلك مشاكل الإدخال/الإخراج ومشاكل OpenSSL
  • com.google.test.loops.performance: حلقات التكرار المستخدَمة لاختبار أداء الجهاز على سبيل المثال، قد يتم تشغيل إحدى الألعاب بأكثر إعدادات الرسومات تعقيدًا لمعرفة طريقة عمل جهاز جديد.

للسماح لتطبيقك بتشغيل حلقات تحمل التصنيف نفسه، اتّبِع الخطوات التالية:

  • إذا كنت تجري اختبارًا باستخدام "أداة إدارة حلقات الاختبار":

    1. في بيان التطبيق، أضِف سطر البيانات الوصفية التالي واستبدِل LABEL_NAME بتصنيف من اختيارك:

      <meta-data
       android:name="com.google.test.loops.LABEL_NAME"
       android:value="1,3-5" />

      في الحقل android:value، يمكنك تحديد نطاق أو مجموعة من الأعداد الصحيحة من 1 إلى 1024 (الحد الأقصى لعدد التكرارات المسموح به في اختبار واحد) التي تمثّل التكرارات التي تريد تصنيفها. يُرجى العِلم أنّه يتم فهرسة الحلقات بدءًا من 1 وليس 0. على سبيل المثال، ينطبق android:value="1,3-5" LABEL_NAME على الحلقات 1 و3 و4 و5.

    2. في تطبيق "أداة إدارة حلقات الاختبار"، أدخِل تصنيفًا واحدًا أو أكثر في حقل التصنيفات.

  • إذا كنت تجري اختبارًا باستخدام وحدة تحكّم Firebase، أدخِل تصنيفًا واحدًا أو أكثر في حقل التصنيفات.

  • إذا كنت تجري اختبارًا باستخدام gcloud CLI، حدِّد تصنيفًا واحدًا أو أكثر للسيناريو باستخدام العلامة --scenario-labels (على سبيل المثال، --scenario-labels=performance,gpu).

دعم منح تراخيص التطبيقات

يتوافق Test Lab مع التطبيقات التي تستخدم خدمة ترخيص التطبيقات التي يقدّمها Google Play. للتأكّد من نجاح عملية التحقّق من الترخيص عند اختبار تطبيقك باستخدام Test Lab، يجب نشر تطبيقك في قناة الإصدار العلني على "متجر Play". لاختبار تطبيقك في قناة الإصدار الأولي أو الإصدار التجريبي باستخدام Test Lab، عليك إزالة عملية التحقّق من الترخيص قبل تحميل تطبيقك إلى Test Lab.

المشكلات المعروفة

تتضمّن اختبارات حلقة الألعاب في Test Lab المشاكل المعروفة التالية:

  • لا تتوافق بعض الأعطال مع عمليات تتبُّع تسلسل استدعاء الدوال البرمجية. على سبيل المثال، قد تحظر بعض إصدارات التطبيق إخراج عملية debuggerd باستخدام prctl(PR_SET_DUMPABLE, 0). لمزيد من المعلومات، يُرجى الاطّلاع على debuggerd.
  • لا يتوافق المستوى 19 من واجهة برمجة التطبيقات حاليًا مع الأجهزة بسبب أخطاء في أذونات الملفات.