دليل مطوّري البرامج حول وقت تشغيل حزمة تطوير البرامج (SDK)

أثناء الاطّلاع على مستندات "مبادرة حماية الخصوصية" على Android، استخدِم الزر معاينة المطوّر أو الزر إصدار تجريبي لاختيار إصدار البرنامج الذي تعمل معه، لأنّ التعليمات قد تختلف.


تقديم ملاحظات

يسمح "وقت تشغيل SDK" بتشغيل حِزم SDK في وضع حماية مخصَّص ومنفصل عن تطبيق الاتصال. ويوفّر "وقت تشغيل SDK" إجراءات حماية وضمانات محسّنة بشأن جمع بيانات المستخدمين. ويتم ذلك من خلال بيئة تنفيذ معدَّلة تحدّ من حقوق الوصول إلى البيانات ومجموعة الأذونات المسموح بها. اطّلِع على مزيد من المعلومات عن "وقت تشغيل SDK" في اقتراح التصميم.

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

القيود المعروفة

للحصول على قائمة بالإمكانات قيد التقدم في "وقت تشغيل SDK"، يمكنك الاطّلاع على ملاحظات الإصدار.

من المتوقع إصلاح القيود التالية في الإصدار الرئيسي التالي لنظام Android الأساسي.

  • عرض الإعلانات ضمن شاشة عرض قابلة للتمرير على سبيل المثال، لا يعمل RecyclerView بشكل صحيح.
    • قد يحدث التشويش عند تغيير الحجم.
    • لا يتم تمرير أحداث التمرير باللمس للمستخدم إلى وقت التشغيل بشكل صحيح.
  • واجهة برمجة التطبيقات Storage

سيتم حلّ المشكلة التالية في عام 2023:

  • لا تعمل واجهتا برمجة التطبيقات getAdId وgetAppSetId بشكل صحيح لأنّه لم يتم بعد تفعيل إمكانية استخدامهما.

قبل البدء

قبل البدء، أكمل الخطوات التالية:

  1. إعداد بيئة تطوير "مبادرة حماية الخصوصية" على Android لا تزال الأدوات اللازمة لدعم "وقت تشغيل SDK" قيد التطوير النشط، لذا سيتطلب منك هذا الدليل استخدام أحدث إصدار Canary من Android استوديو. يمكنك تشغيل هذا الإصدار من "استوديو Android" بالتوازي مع الإصدارات الأخرى التي تستخدمها، لذا يُرجى إعلامنا إذا لم يكن هذا الشرط مناسبًا لك.

  2. يمكنك إما تثبيت صورة نظام على جهاز متوافق أو إعداد محاكي يتيح استخدام "مبادرة حماية الخصوصية" على Android.

إعداد مشروعك في "استوديو Android"

لتجربة "وقت تشغيل SDK"، يمكنك استخدام نموذج مشابه لنموذج خادم العميل. يتمثل الاختلاف الرئيسي في أن التطبيقات (العميل) وحزم SDK ("الخادم") تعمل على نفس الجهاز.

  1. أضِف وحدة تطبيق إلى مشروعك. هذه الوحدة هي البرنامج الذي يشغِّل حزمة تطوير البرامج (SDK).
  2. في وحدة تطبيقك، فعِّل "وقت تشغيل SDK" وحدِّد الأذونات اللازمة واضبط الخدمات الإعلانية الخاصة بواجهة برمجة التطبيقات.
  3. أضف وحدة مكتبة واحدة إلى مشروعك. تحتوي هذه الوحدة على رمز حزمة تطوير البرامج (SDK).
  4. في وحدة حزمة تطوير البرامج (SDK)، حدِّد الأذونات اللازمة. ولن تحتاج إلى ضبط الخدمات الإعلانية الخاصة بواجهة برمجة التطبيقات في هذه الوحدة.
  5. عليك إزالة العلامة dependencies في ملف build.gradle الخاص بوحدة المكتبة والذي لا تستخدمه حزمة تطوير البرامج (SDK). في معظم الحالات، يمكنك إزالة جميع التبعيات. يمكنك القيام بذلك عن طريق إنشاء دليل جديد يتجاوب اسمه مع SDK الخاص بك.
  6. إنشاء وحدة جديدة يدويًا باستخدام النوع com.android.privacy-sandbox-sdk يتم إرفاق هذا الرمز مع رمز SDK لإنشاء حزمة APK يمكن نشرها على جهازك. يمكنك القيام بذلك عن طريق إنشاء دليل جديد يتجاوب اسمه مع SDK لديك. يجب إضافة ملف build.gradle فارغ. ستتم تعبئة محتوى هذا الملف لاحقًا في هذا الدليل.

  7. أضِف المقتطف التالي إلى ملف gradle.properties الخاص بك:

    android.experimental.privacysandboxsdk.enable=true
    

  8. يُرجى تنزيل صورة المحاكي Tiramisu (مستوى الإضافة 4) وإنشاء محاكي بهذه الصورة التي تتضمّن تطبيق "متجر Play".

قد يكون لديك إعداد نهائي مختلف عن الإعداد الموضّح في الفقرة السابقة، وذلك بناءً على ما إذا كنت مطوِّر حزمة SDK أو مطوِّر تطبيقات.

ثبِّت حزمة SDK على جهاز اختباري بالطريقة نفسها التي يتم بها تثبيت التطبيق، وذلك باستخدام "استوديو Android" أو Android Debug Bridge (ADB). لمساعدتك في البدء، أنشأنا نماذج من التطبيقات بلغتَي البرمجة Kotlin وJava، ويمكن العثور عليها في مستودع GitHub هذا. تحتوي ملفات README وملفات البيان على تعليقات تصف ما يجب تغييره لتشغيل العيّنة في الإصدارات الثابتة من "استوديو Android".

إعداد حزمة تطوير البرامج (SDK)

  1. إنشاء دليل على مستوى الوحدة يدويًا. يعمل هذا كبرنامج تضمين حول رمز التنفيذ لإنشاء حزمة SDK لحزمة SDK. في الدليل الجديد، أضِف ملف build.gradle واملأه بالمقتطف التالي. استخدِم اسمًا فريدًا لحزمة تطوير البرامج (SDK) التي يتم تفعيلها في وقت التشغيل (RE-SDK) وقدِّم نسخة منها. ضمِّن وحدة مكتبتك في قسم dependencies.

    plugins {
        id 'com.android.privacy-sandbox-sdk'
    }
    
    android {
        compileSdk 33
        compileSdkExtension 4
        minSdk 33
        targetSdk 33
        namespace = "com.example.example-sdk"
    
        bundle {
            packageName = "com.example.privacysandbox.provider"
            sdkProviderClassName = "com.example.sdk_implementation.SdkProviderImpl"
            setVersion(1, 0, 0)
        }
    }
    
    dependencies {
        include project(':<your-library-here>')
    }
    
  2. أنشئ فئة في مكتبة التنفيذ لتكون بمثابة نقطة دخول لحزمة تطوير البرامج (SDK). ويجب أن يرتبط اسم الفئة بقيمة sdkProviderClassName وأن يوسّع نطاق SandboxedSdkProvider.

تم توسيع نقطة دخول حزمة تطوير البرامج (SDK) في SandboxedSdkProvider. يحتوي SandboxedSdkProvider على عنصر Context لحزمة تطوير البرامج (SDK) الخاصة بك، والذي يمكنك الوصول إليه عن طريق طلب getContext(). يجب الوصول إلى هذا السياق مرة واحدة فقط عند استدعاء onLoadSdk().

لتجميع تطبيق SDK، يجب إلغاء الطرق للتعامل مع دورة حياة SDK:

onLoadSdk()

تحمِّل هذه الحزمة حزمة تطوير البرامج (SDK) في وضع الحماية، وترسل إشعارًا إلى تطبيق الاتصال عندما تكون حزمة SDK جاهزة للتعامل مع الطلبات من خلال ضبط واجهتها على أنّها كائن IBinder مُحاط بكائن SandboxedSdk جديد. يقدّم دليل خدمات الربط طرقًا مختلفة لتقديم السمة IBinder. لديك مرونة لاختيار طريقتك، ولكن يجب أن تكون متسقة مع SDK وتطبيق الاتصال.

باستخدام AIDL كمثال، يجب تحديد ملف AIDL لعرض IBinder الذي ستتم مشاركته واستخدامه من التطبيق:

// ISdkInterface.aidl
interface ISdkInterface {
    // the public functions to share with the App.
    int doSomething();
}
getView()

ينشئ طريقة العرض لإعلانك وتضبط إعداداتها، ويفعّل العرض بنفس طريقة أي طريقة عرض أخرى في Android، ثم يُرجع العرض ليتم عرضه عن بُعد في نافذة بعرض وارتفاع محدَّدين بالبكسل.

يوضح مقتطف الرمز التالي كيفية إلغاء هذه الطرق:

Kotlin

class SdkProviderImpl : SandboxedSdkProvider() {
    override fun onLoadSdk(params: Bundle?): SandboxedSdk {
        // Returns a SandboxedSdk, passed back to the client. The IBinder used
        // to create the SandboxedSdk object is used by the app to call into the
        // SDK.
        return SandboxedSdk(SdkInterfaceProxy())
    }

    override fun getView(windowContext: Context, bundle: Bundle, width: Int,
            height: Int): View {
        val webView = WebView(windowContext)
        val layoutParams = LinearLayout.LayoutParams(width, height)
        webView.setLayoutParams(layoutParams)
        webView.loadUrl("https://developer.android.com/privacy-sandbox")
        return webView
    }

    private class SdkInterfaceProxy : ISdkInterface.Stub() {
        fun doSomething() {
            // Implementation of the API.
        }
    }
}

Java

public class SdkProviderImpl extends SandboxedSdkProvider {
    @Override
    public SandboxedSdk onLoadSdk(Bundle params) {
        // Returns a SandboxedSdk, passed back to the client. The IBinder used
        // to create the SandboxedSdk object is used by the app to call into the
        // SDK.
        return new SandboxedSdk(new SdkInterfaceProxy());
    }

    @Override
    public View getView(Context windowContext, Bundle bundle, int width,
            int height) {
        WebView webView = new WebView(windowContext);
        LinearLayout.LayoutParams layoutParams =
                new LinearLayout.LayoutParams(width, height);
        webView.setLayoutParams(layoutParams);
        webView.loadUrl("https://developer.android.com/privacy-sandbox");
        return webView;
    }

    private static class SdkInterfaceProxy extends ISdkInterface.Stub {
        @Override
        public void doSomething() {
            // Implementation of the API.
        }
    }
}

اختبار مشغّلات الفيديو في "وقت تشغيل SDK"

بالإضافة إلى إتاحة إعلانات البانر، تلتزم "مبادرة حماية الخصوصية" بتوفير مشغّلات الفيديو التي تعمل ضمن "وقت تشغيل SDK".

وتتشابه خطوات اختبار مشغّلات الفيديو مع اختبار إعلانات البانر. يمكنك تغيير طريقة getView() لنقطة دخول حزمة تطوير البرامج (SDK) لتضمين مشغّل فيديو في العنصر View الذي تم عرضه. اختبِر جميع مسارات مشغّل الفيديو التي تتوقّع أن تكون متوافقة مع "مبادرة حماية الخصوصية". يُرجى العِلم أنّ التواصل بين حزمة SDK وتطبيق العميل بشأن مراحل نشاط الفيديو ليس ضِمن النطاق، لذا إنّ الملاحظات غير مطلوبة بعد بشأن هذه الوظيفة.

سيضمن اختبارك وملاحظاتك أن يتوافق "وقت تشغيل SDK" مع جميع حالات استخدام مشغّل الفيديو المفضل لديك.

يوضح مقتطف الرمز التالي كيفية عرض عرض فيديو بسيط يتم تحميله من عنوان URL.

Kotlin

    class SdkProviderImpl : SandboxedSdkProvider() {

        override fun getView(windowContext: Context, bundle: Bundle, width: Int,
                height: Int): View {
            val videoView = VideoView(windowContext)
            val layoutParams = LinearLayout.LayoutParams(width, height)
            videoView.setLayoutParams(layoutParams)
            videoView.setVideoURI(Uri.parse("https://test.website/video.mp4"))
            videoView.setOnPreparedListener { mp -> mp.start() }
            return videoView
        }
    }

Java

    public class SdkProviderImpl extends SandboxedSdkProvider {

        @Override
        public View getView(Context windowContext, Bundle bundle, int width,
                int height) {
            VideoView videoView = new VideoView(windowContext);
            LinearLayout.LayoutParams layoutParams =
                    new LinearLayout.LayoutParams(width, height);
            videoView.setLayoutParams(layoutParams);
            videoView.setVideoURI(Uri.parse("https://test.website/video.mp4"));
            videoView.setOnPreparedListener(mp -> {
                mp.start();
            });
            return videoView;
        }
    }

استخدام واجهات برمجة التطبيقات للتخزين في حزمة تطوير البرامج (SDK)

لم يعُد بإمكان حِزم SDK في "وقت تشغيل SDK" الوصول إلى وحدة التخزين الداخلية للتطبيق أو قراءتها أو كتابتها، والعكس صحيح. سيتم تخصيص مساحة التخزين الداخلية الخاصة بها لوقت تشغيل SDK، والتي يمكن ضمان أن تكون منفصلة عن التطبيق.

ستتمكن حِزم تطوير البرامج (SDK) من الوصول إلى وحدة التخزين الداخلية المنفصلة هذه باستخدام واجهات برمجة التطبيقات لتخزين الملفات في العنصر Context الذي يعرضه SandboxedSdkProvider#getContext(). يمكن لحِزم SDK استخدام مساحة تخزين داخلية فقط، لذلك لن تعمل سوى واجهات برمجة تطبيقات مساحة التخزين الداخلية، مثل Context.getFilesDir() أو Context.getCacheDir(). يمكنك الاطّلاع على المزيد من الأمثلة في قسم الوصول من وحدة التخزين الداخلية.

لا يمكن الوصول إلى وحدة التخزين الخارجية من "وقت تشغيل SDK". سيؤدي استدعاء واجهات برمجة التطبيقات للوصول إلى وحدة التخزين الخارجية إما إلى إنشاء استثناء أو عرض null. عدة أمثلة:

في نظام التشغيل Android 13، ستشارك كل حِزم SDK في "وقت تشغيل SDK" وحدة التخزين الداخلية المخصّصة لوقت تشغيل SDK. وسيستمر التخزين إلى أن يتم إلغاء تثبيت تطبيق العميل أو عند تنظيف بيانات تطبيق العميل.

عليك استخدام Context التي تم إرجاعها من خلال SandboxedSdkProvider.getContext() لتوفير مساحة التخزين. لا نضمن لك عمل واجهة برمجة التطبيقات الخاصة بمساحة تخزين الملفات مع أي مثيل آخر من عناصر Context، مثل سياق التطبيق، على النحو المتوقَّع في جميع الحالات أو في المستقبل.

يوضّح مقتطف الرمز التالي كيفية استخدام مساحة التخزين في "وقت تشغيل SDK":

Kotlin

    private static class SdkInterfaceStorage extends ISdkInterface.Stub {
    override fun doSomething() {
        val filename = "myfile"
        val fileContents = "content"
        try {
            getContext().openFileOutput(filename, Context.MODE_PRIVATE).use {
                it.write(fileContents.toByteArray())
            } catch (e: Exception) {
                throw RuntimeException(e)
            }
        }
    }
}

    

Java

    private static class SdkInterfaceStorage extends ISdkInterface.Stub {
    @Override
    public void doSomething() {
        final filename = "myFile";
        final String fileContents = "content";
        try (FileOutputStream fos = getContext().openFileOutput(filename, Context.MODE_PRIVATE)) {
            fos.write(fileContents.toByteArray());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

    

مساحة تخزين لكل حزمة SDK

تتضمن كل حزمة SDK دليل مساحة تخزين خاصًا بها ضمن مساحة التخزين الداخلية المنفصلة لكل "وقت تشغيل SDK". مساحة التخزين لكل حزمة SDK هي فرز منطقي لمساحة التخزين الداخلية في "وقت تشغيل SDK"، والتي تساعد في حساب حجم مساحة التخزين التي تستخدمها كل حزمة SDK.

في نظام التشغيل Android 13، تعرض واجهة برمجة تطبيقات واحدة فقط مسارًا إلى مساحة التخزين لكل حزمة SDK: Context#getDataDir().

في نظام التشغيل Android 14، تعرض جميع واجهات برمجة التطبيقات لمساحة التخزين الداخلية في العنصر Context مسار تخزين لكل حزمة SDK. قد تحتاج إلى تفعيل هذه الميزة عن طريق تشغيل أمر adb التالي:

adb shell device_config put adservices sdksandbox_customized_sdk_context_enabled true

الوصول إلى المعرِّف الإعلاني المقدَّم من "خدمات Google Play"

إذا كانت حزمة تطوير البرامج (SDK) بحاجة إلى الوصول إلى المعرِّف الإعلاني الذي تقدِّمه "خدمات Google Play":

  • يُرجى تقديم بيان عن إذن "android.permission.ACCESS_ADSERVICES_AD_ID" في ملف بيان حزمة تطوير البرامج (SDK).
  • استخدِم AdIdManager#getAdId() لاسترداد القيمة بشكل غير متزامن.

الوصول إلى معرّف مجموعة التطبيقات المقدَّم من "خدمات Google Play"

إذا كانت حزمة تطوير البرامج (SDK) بحاجة إلى الوصول إلى معرّف مجموعة التطبيقات الذي توفّره "خدمات Google Play":

  • استخدِم AppSetIdManager#getAppSetId() لاسترداد القيمة بشكل غير متزامن.

تحديث تطبيقات العملاء

لطلب حزمة تطوير برامج (SDK) تعمل في وقت تشغيل SDK، عليك إجراء التغييرات التالية على تطبيق عميل الاتصال:

  1. أضِف إذنَي INTERNET وACCESS_NETWORK_STATE إلى بيان التطبيق:

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    
  2. في النشاط الذي يتضمّن إعلانًا في تطبيقك، يجب الإشارة إلى SdkSandboxManager، وهي قيمة منطقية لمعرفة ما إذا تم تحميل حزمة تطوير البرامج (SDK) وكائن SurfaceView للعرض عن بُعد:

    Kotlin

        private lateinit var mSdkSandboxManager: SdkSandboxManager
        private lateinit var mClientView: SurfaceView
        private var mSdkLoaded = false
    
        companion object {
            private const val SDK_NAME = "com.example.privacysandbox.provider"
        }
    

    Java

        private static final String SDK_NAME = "com.example.privacysandbox.provider";
    
        private SdkSandboxManager mSdkSandboxManager;
        private SurfaceView mClientView;
        private boolean mSdkLoaded = false;
    
  3. تحقَّق من توفُّر عملية تشغيل SDK على الجهاز.

    1. تحقَّق من الثابت SdkSandboxState (getSdkSandboxState()). SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION يعني أنّ "وقت تشغيل SDK" متاح.

    2. تحقَّق من إتمام الاتصال برقم loadSdk(). ويكون الإجراء ناجحًا إذا لم يتم طرح أي استثناءات، والمستلِم هو مثال SandboxedSdk.

      • عليك طلب "loadSdk()" من المقدّمة. وإذا تم طلبها من الخلفية، سيتم طرح SecurityException.

      • تحقَّق من OutcomeReceiver للاطّلاع على مثيل SandboxedSdk للتأكّد مما إذا تم إسقاط LoadSdkException. يشير الاستثناءات إلى أنّ "وقت تشغيل SDK" قد لا يكون متاحًا.

    إذا تعذَّر استدعاء SdkSandboxState أو loadSdk، لن يكون "وقت تشغيل SDK" متاحًا، ومن المفترض أن يعود الاستدعاء إلى حزمة تطوير البرامج (SDK) الحالية.

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

    Kotlin

        private inner class LoadSdkOutcomeReceiverImpl private constructor() :
                OutcomeReceiver {
    
          override fun onResult(sandboxedSdk: SandboxedSdk) {
              mSdkLoaded = true
    
              val binder: IBinder = sandboxedSdk.getInterface()
              if (!binderInterface.isPresent()) {
                  // SDK is not loaded anymore.
                  return
              }
              val sdkInterface: ISdkInterface = ISdkInterface.Stub.asInterface(binder)
              sdkInterface.doSomething()
    
              Handler(Looper.getMainLooper()).post {
                  val bundle = Bundle()
                  bundle.putInt(SdkSandboxManager.EXTRA_WIDTH_IN_PIXELS, mClientView.getWidth())
                  bundle.putInt(SdkSandboxManager.EXTRA_HEIGHT_IN_PIXELS, mClientView.getHeight())
                  bundle.putInt(SdkSandboxManager.EXTRA_DISPLAY_ID, display!!.displayId)
                  bundle.putInt(SdkSandboxManager.EXTRA_HOST_TOKEN, mClientView.getHostToken())
                  mSdkSandboxManager!!.requestSurfacePackage(
                          SDK_NAME, bundle, { obj: Runnable -> obj.run() },
                          RequestSurfacePackageOutcomeReceiverImpl())
              }
          }
    
          override fun onError(error: LoadSdkException) {
                  // Log or show error.
          }
        }
    

    Java

        import static android.app.sdksandbox.SdkSandboxManager.EXTRA_DISPLAY_ID;
        import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HEIGHT_IN_PIXELS;
        import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HOST_TOKEN;
        import static android.app.sdksandbox.SdkSandboxManager.EXTRA_WIDTH_IN_PIXELS;
    
        private class LoadSdkOutcomeReceiverImpl
                implements OutcomeReceiver {
            private LoadSdkOutcomeReceiverImpl() {}
    
            @Override
            public void onResult(@NonNull SandboxedSdk sandboxedSdk) {
                mSdkLoaded = true;
    
                IBinder binder = sandboxedSdk.getInterface();
                if (!binderInterface.isPresent()) {
                    // SDK is not loaded anymore.
                    return;
                }
                ISdkInterface sdkInterface = ISdkInterface.Stub.asInterface(binder);
                sdkInterface.doSomething();
    
                new Handler(Looper.getMainLooper()).post(() -> {
                    Bundle bundle = new Bundle();
                    bundle.putInt(EXTRA_WIDTH_IN_PIXELS, mClientView.getWidth());
                    bundle.putInt(EXTRA_HEIGHT_IN_PIXELS, mClientView.getHeight());
                    bundle.putInt(EXTRA_DISPLAY_ID, getDisplay().getDisplayId());
                    bundle.putInt(EXTRA_HOST_TOKEN, mClientView.getHostToken());
    
                    mSdkSandboxManager.requestSurfacePackage(
                            SDK_NAME, bundle, Runnable::run,
                            new RequestSurfacePackageOutcomeReceiverImpl());
                });
            }
    
            @Override
            public void onError(@NonNull LoadSdkException error) {
                // Log or show error.
            }
        }
    

    لاسترداد عرض عن بُعد من حزمة تطوير البرامج (SDK) في وقت التشغيل أثناء طلب requestSurfacePackage()، عليك تنفيذ واجهة OutcomeReceiver<Bundle, RequestSurfacePackageException>:

    Kotlin

        private inner class RequestSurfacePackageOutcomeReceiverImpl :
                OutcomeReceiver {
            fun onResult(@NonNull result: Bundle) {
                Handler(Looper.getMainLooper())
                        .post {
                            val surfacePackage: SurfacePackage = result.getParcelable(
                                    EXTRA_SURFACE_PACKAGE,
                                    SurfacePackage::class.java)
                            mRenderedView.setChildSurfacePackage(surfacePackage)
                            mRenderedView.setVisibility(View.VISIBLE)
                        }
            }
    
            fun onError(@NonNull error: RequestSurfacePackageException?) {
                // Error handling
            }
        }
    

    Java

        import static android.app.sdksandbox.SdkSandboxManager.EXTRA_SURFACE_PACKAGE;
    
        private class RequestSurfacePackageOutcomeReceiverImpl
                implements OutcomeReceiver {
            @Override
            public void onResult(@NonNull Bundle result) {
                new Handler(Looper.getMainLooper())
                        .post(
                                () -> {
                                    SurfacePackage surfacePackage =
                                            result.getParcelable(
                                                    EXTRA_SURFACE_PACKAGE,
                                                    SurfacePackage.class);
                                    mRenderedView.setChildSurfacePackage(surfacePackage);
                                    mRenderedView.setVisibility(View.VISIBLE);
                                });
            }
            @Override
            public void onError(@NonNull RequestSurfacePackageException error) {
                // Error handling
            }
        }
    

    عند الانتهاء من إظهار الرؤية، تذكَّر رفع إصبعك عن "SurfacePackage" عن طريق الاتصال على الرقم التالي:

    surfacePackage.notifyDetachedFromWindow()
    
  5. في onCreate()، يمكنك تهيئة SdkSandboxManager وعمليات معاودة الاتصال اللازمة، ثم تقديم طلب لتحميل حزمة SDK:

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mSdkSandboxManager = applicationContext.getSystemService(
                SdkSandboxManager::class.java
        )
    
        mClientView = findViewById(R.id.rendered_view)
        mClientView.setZOrderOnTop(true)
    
        val loadSdkCallback = LoadSdkCallbackImpl()
        mSdkSandboxManager.loadSdk(
                SDK_NAME, Bundle(), { obj: Runnable -> obj.run() }, loadSdkCallback
        )
    }
    

    Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        mSdkSandboxManager = getApplicationContext().getSystemService(
                SdkSandboxManager.class);
    
        mClientView = findViewById(R.id.rendered_view);
        mClientView.setZOrderOnTop(true);
    
        LoadSdkCallbackImpl loadSdkCallback = new LoadSdkCallbackImpl();
        mSdkSandboxManager.loadSdk(
                SDK_NAME, new Bundle(), Runnable::run, loadSdkCallback);
    }
    
  6. لمعالجة حالة انتهاء عملية وضع الحماية لحزمة SDK بشكل غير متوقع، حدِّد عملية تنفيذ لواجهة SdkSandboxProcessDeathCallback:

    Kotlin

        private inner class SdkSandboxLifecycleCallbackImpl() : SdkSandboxProcessDeathCallback {
            override fun onSdkSandboxDied() {
                // The SDK runtime process has terminated. To bring back up the
                // sandbox and continue using SDKs, load the SDKs again.
                val loadSdkCallback = LoadSdkOutcomeReceiverImpl()
                mSdkSandboxManager.loadSdk(
                          SDK_NAME, Bundle(), { obj: Runnable -> obj.run() },
                          loadSdkCallback)
            }
        }
    

    Java

          private class SdkSandboxLifecycleCallbackImpl
                  implements SdkSandboxProcessDeathCallback {
              @Override
              public void onSdkSandboxDied() {
                  // The SDK runtime process has terminated. To bring back up
                  // the sandbox and continue using SDKs, load the SDKs again.
                  LoadSdkOutcomeReceiverImpl loadSdkCallback =
                          new LoadSdkOutcomeReceiverImpl();
                  mSdkSandboxManager.loadSdk(
                              SDK_NAME, new Bundle(), Runnable::run, loadSdkCallback);
              }
          }
    

    لتسجيل معاودة الاتصال هذه لتلقي معلومات حول وقت إنهاء وضع الحماية لحزمة SDK، أضف السطر التالي في أي وقت:

    Kotlin

        mSdkSandboxManager.addSdkSandboxProcessDeathCallback({ obj: Runnable -> obj.run() },
                SdkSandboxLifecycleCallbackImpl())
    

    Java

        mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run,
                new SdkSandboxLifecycleCallbackImpl());
    

    ونظرًا لفقدان حالة وضع الحماية عند إنهاء العملية، قد لا تعمل طرق العرض التي تم عرضها عن بُعد بواسطة حزمة SDK بشكل صحيح. لمواصلة التفاعل مع حِزم SDK، يجب تحميل طرق العرض هذه مرة أخرى لبدء عملية وضع حماية جديدة.

  7. أضِف عنصر الاعتماد إلى وحدة حزمة تطوير البرامج (SDK) في build.gradle في تطبيق العميل:

    dependencies {
        ...
        implementation project(':<your-sdk-module>')
        ...
    }

اختبار تطبيقاتك

لتشغيل تطبيق العميل، يجب تثبيت تطبيق SDK وتطبيق العميل على جهاز الاختبار باستخدام Android Studio أو سطر الأوامر.

النشر من خلال "استوديو Android"

عند النشر من خلال "استوديو Android"، أكمل الخطوات التالية:

  1. افتح مشروع "استوديو Android" لتطبيق عميلك.
  2. انتقِل إلى تشغيل > تعديل عمليات الضبط. تظهر نافذة إعداد التشغيل/تصحيح الأخطاء.
  3. ضمن خيارات الإطلاق، اضبط إطلاق على نشاط محدّد.
  4. انقر على قائمة الخيارات الإضافية بجانب "النشاط" واختَر النشاط الرئيسي لعميلك.
  5. انقر على تطبيق ثم حسنًا.
  6. انقر على تشغيل لتثبيت تطبيق العميل وحزمة تطوير البرامج (SDK) على جهاز الاختبار.

النشر في سطر الأوامر

عند النشر باستخدام سطر الأوامر، أكمل الخطوات الواردة في القائمة التالية. يفترض هذا القسم أنّ اسم وحدة تطبيق حزمة تطوير البرامج (SDK) هو sdk-app وأنّ اسم وحدة تطبيق العميل هو client-app.

  1. من الوحدة الطرفية لسطر الأوامر، يمكنك إنشاء حِزم APK لحزمة تطوير البرامج (SDK) ضمن "مبادرة حماية الخصوصية":

    ./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
    

    يؤدي هذا إلى عرض الموقع الجغرافي لملفات APK التي تم إنشاؤها. تم توقيع حِزم APK هذه باستخدام مفتاح تصحيح الأخطاء المحلي. ستحتاج إلى هذا المسار في الأمر التالي.

  2. تثبيت حزمة APK على جهازك:

    adb install -t /path/to/your/standalone.apk
    
  3. في "استوديو Android"، انقر على تشغيل > تعديل عمليات الضبط. ستظهر نافذة التشغيل/تصحيح الأخطاء.

  4. ضمن خيارات التثبيت، اضبط نشر على حزمة APK التلقائية.

  5. انقر على تطبيق ثم حسنًا.

  6. انقر على Run (تشغيل) لتثبيت حزمة APK على جهاز الاختبار.

تصحيح أخطاء تطبيقاتك

لتصحيح أخطاء تطبيق العميل، انقر على الزر تصحيح الأخطاء في "استوديو Android".

لتصحيح أخطاء تطبيق SDK، انتقل إلى Run > Attach to Process (تشغيل > إرفاق بالعملية)، حيث تظهر لك شاشة منبثقة (كما هو موضح أدناه). ضَع علامة في المربّع عرض جميع العمليات. في القائمة التي تظهر، ابحث عن عملية تسمى CLIENT_APP_PROCESS_sdk_sandbox. حدد هذا الخيار وأضف نقاط التوقف في التعليمات البرمجية لتطبيق SDK لبدء تصحيح أخطاء SDK.

تظهر عملية تطبيق حزمة تطوير البرامج (SDK) في عرض على شكل قائمة بالقرب من أسفل مربّع الحوار.
شاشة اختيار العملية، التي يمكنك من خلالها اختيار تطبيق حزمة تطوير البرامج (SDK) لتصحيح الأخطاء فيه.

بدء وقت تشغيل حزمة SDK وإيقافه من سطر الأوامر

لبدء عملية تشغيل حزمة تطوير البرامج (SDK) في تطبيقك، استخدِم أمر PowerShell التالي:

adb shell cmd sdk_sandbox start [--user <USER_ID> | current] <CLIENT_APP_PACKAGE>

وبالمثل، لإيقاف عملية تشغيل حزمة تطوير البرامج (SDK)، شغِّل الأمر التالي:

adb shell cmd sdk_sandbox stop [--user <USER_ID> | current] <CLIENT_APP_PACKAGE>

القيود

للحصول على قائمة بالإمكانات قيد التقدم في "وقت تشغيل SDK"، يمكنك الاطّلاع على ملاحظات الإصدار.

عيّنات تعليمات برمجية

يحتوي مستودع واجهات برمجة التطبيقات (API) الخاصة بـ "وقت تشغيل SDK" و"الحفاظ على الخصوصية" على GitHub على مجموعة من المشاريع الفردية الخاصة بـ "استوديو Android" لمساعدتك في البدء، بما في ذلك نماذج توضّح كيفية إعداد "وقت تشغيل SDK" واستدعاءه.

الإبلاغ عن الأخطاء والمشاكل

ملاحظاتك هي جزء مهم من "مبادرة حماية الخصوصية" على Android. يُرجى إعلامنا بأي مشاكل تصادفها أو أفكارًا لتحسين "مبادرة حماية الخصوصية" على Android.