أثناء الاطّلاع على مستندات "مبادرة حماية الخصوصية" على Android، استخدِم الزر معاينة المطوّر أو الزر إصدار تجريبي لاختيار إصدار البرنامج الذي تعمل معه، لأنّ التعليمات قد تختلف.
يسمح "وقت تشغيل SDK" بتشغيل حِزم SDK في وضع حماية مخصَّص ومنفصل عن تطبيق الاتصال. ويوفّر "وقت تشغيل SDK" إجراءات حماية وضمانات محسّنة بشأن جمع بيانات المستخدمين. ويتم ذلك من خلال بيئة تنفيذ معدَّلة تحدّ من حقوق الوصول إلى البيانات ومجموعة الأذونات المسموح بها. اطّلِع على مزيد من المعلومات عن "وقت تشغيل SDK" في اقتراح التصميم.
ترشدك الخطوات الواردة في هذه الصفحة خلال عملية إنشاء حزمة تطوير برامج (SDK) يتم تفعيلها في وقت التشغيل وتحدّد عرضًا مستنِدًا إلى الويب يمكن عرضه عن بُعد في تطبيق اتصال.
القيود المعروفة
للحصول على قائمة بالإمكانات قيد التقدم في "وقت تشغيل SDK"، يمكنك الاطّلاع على ملاحظات الإصدار.
من المتوقع إصلاح القيود التالية في الإصدار الرئيسي التالي لنظام Android الأساسي.
- عرض الإعلانات ضمن شاشة عرض قابلة للتمرير على سبيل المثال، لا يعمل
RecyclerView
بشكل صحيح.- قد يحدث التشويش عند تغيير الحجم.
- لا يتم تمرير أحداث التمرير باللمس للمستخدم إلى وقت التشغيل بشكل صحيح.
- واجهة برمجة التطبيقات Storage
- لا تتوفّر مساحة التخزين لكل حزمة تطوير برامج (SDK) في Android 13.
سيتم حلّ المشكلة التالية في عام 2023:
- لا تعمل واجهتا برمجة التطبيقات
getAdId
وgetAppSetId
بشكل صحيح لأنّه لم يتم بعد تفعيل إمكانية استخدامهما.
قبل البدء
قبل البدء، أكمل الخطوات التالية:
إعداد بيئة تطوير "مبادرة حماية الخصوصية" على Android لا تزال الأدوات اللازمة لدعم "وقت تشغيل SDK" قيد التطوير النشط، لذا سيُطلب منك في هذا الدليل استخدام أحدث إصدار Canary من Android استوديو. يمكنك تشغيل هذا الإصدار من "استوديو Android" بالتوازي مع الإصدارات الأخرى التي تستخدمها، لذا يُرجى إعلامنا إذا لم يكن هذا الشرط مناسبًا لك.
يمكنك إما تثبيت صورة نظام على جهاز متوافق أو إعداد محاكي يتيح استخدام "مبادرة حماية الخصوصية" على Android.
إعداد مشروعك في "استوديو Android"
لتجربة "وقت تشغيل SDK"، يمكنك استخدام نموذج مشابه لنموذج خادم العميل. يتمثل الاختلاف الرئيسي في أن التطبيقات (العميل) وحزم SDK ("الخادم") تعمل على نفس الجهاز.
- أضِف وحدة تطبيق إلى مشروعك. هذه الوحدة هي البرنامج الذي يشغِّل حزمة تطوير البرامج (SDK).
- في وحدة تطبيقك، فعِّل "وقت تشغيل SDK" وحدِّد الأذونات اللازمة واضبط الخدمات الإعلانية الخاصة بواجهة برمجة التطبيقات.
- أضف وحدة مكتبة واحدة إلى مشروعك. تحتوي هذه الوحدة على رمز حزمة تطوير البرامج (SDK).
- في وحدة حزمة تطوير البرامج (SDK)، حدِّد الأذونات اللازمة. ولن تحتاج إلى ضبط الخدمات الإعلانية الخاصة بواجهة برمجة التطبيقات في هذه الوحدة.
- عليك إزالة العلامة
dependencies
في ملفbuild.gradle
الخاص بوحدة المكتبة والذي لا تستخدمه حزمة تطوير البرامج (SDK). في معظم الحالات، يمكنك إزالة جميع التبعيات. يمكنك القيام بذلك عن طريق إنشاء دليل جديد يتجاوب اسمه مع SDK الخاص بك. إنشاء وحدة جديدة يدويًا باستخدام النوع
com.android.privacy-sandbox-sdk
يتم إرفاق هذا الرمز مع رمز SDK لإنشاء حزمة APK يمكن نشرها على جهازك. يمكنك القيام بذلك عن طريق إنشاء دليل جديد يتجاوب اسمه مع SDK لديك. يجب إضافة ملفbuild.gradle
فارغ. ستتم تعبئة محتوى هذا الملف لاحقًا في هذا الدليل.أضِف المقتطف التالي إلى ملف
gradle.properties
الخاص بك:android.experimental.privacysandboxsdk.enable=true
نزِّل صورة المحاكي TiramisuPrivacySandbox وأنشِئ محاكيًا بهذه الصورة التي تتضمّن "متجر Play".
قد يكون لديك إعداد نهائي مختلف عن الإعداد الموضّح في الفقرة السابقة، وذلك بناءً على ما إذا كنت مطوِّر حزمة SDK أو مطوِّر تطبيقات.
ثبِّت حزمة SDK على جهاز اختباري بالطريقة نفسها التي يتم بها تثبيت التطبيق، وذلك باستخدام "استوديو Android" أو Android Debug Bridge (ADB). لمساعدتك في البدء، أنشأنا نماذج من التطبيقات بلغتَي البرمجة Kotlin وJava، ويمكن العثور عليها في مستودع GitHub هذا. تحتوي ملفات README وملفات البيان على تعليقات تصف ما يجب تغييره لتشغيل العيّنة في الإصدارات الثابتة من "استوديو Android".
إعداد حزمة تطوير البرامج (SDK)
إنشاء دليل على مستوى الوحدة يدويًا. يعمل هذا كبرنامج تضمين حول رمز التنفيذ لإنشاء حزمة SDK لحزمة SDK. في الدليل الجديد، أضِف ملف
build.gradle
واملأه بالمقتطف التالي. استخدِم اسمًا فريدًا لحزمة تطوير البرامج (SDK) التي يتم تفعيلها في وقت التشغيل (RE-SDK) وقدِّم نسخة منها. ضمِّن وحدة مكتبتك في قسمdependencies
.plugins { id 'com.android.privacy-sandbox-sdk' } android { compileSdkPreview 'TiramisuPrivacySandbox' minSdkPreview 'TiramisuPrivacySandbox' 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>') }
أنشئ فئة في مكتبة التنفيذ لتكون بمثابة نقطة دخول لحزمة تطوير البرامج (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. } } }
SdkSandboxController
SdkSandboxController
هو برنامج تضمين خدمة نظام مستند إلى السياق متوفر لحِزم SDK. ويمكن جلب هذه الحزمة بواسطة حِزم SDK باستخدام السياق المُستلَم من
SandboxedSdkProvider#getContext()
واستدعاء
context.getSystemService(SdkSandboxController.class)
في هذا السياق. وتتضمّن وحدة التحكّم واجهات برمجة تطبيقات تساعد حِزم SDK على التفاعل مع "مبادرة حماية الخصوصية" والحصول على المعلومات منها.
تعرض معظم واجهات برمجة التطبيقات في وحدة التحكّم استثناءات إذا لم يكن SandboxedSdkContext
هو السياق المستخدَم للوصول إلى البيانات. نحن نخطط لجعل برنامج تضمين الخدمة غير متاح للسياقات الأخرى. تم تصميم SdkSandboxController
ليستخدمه موفّرو حِزم SDK،
ولا يُنصح باستخدامه من قِبل مطوّري التطبيقات.
الاتصال من حزمة SDK إلى حزمة تطوير البرامج (SDK)
يجب أن تتمكن حِزم SDK في وقت التشغيل من التواصل بعضها مع بعض لدعم التوسّط وحالات الاستخدام ذات الصلة. توفر مجموعة الأدوات الموضحة هنا واجهة للعمل مع حزم SDK المتوفرة لحزم SDK الأخرى داخل وضع الحماية. يُعتبر تنفيذ "وقت تشغيل SDK" خطوة أولى نحو تفعيل التواصل باستخدام حزمة SDK، وقد لا يشمل حتى الآن جميع حالات الاستخدام للتوسّط في "مبادرة حماية الخصوصية".
توفّر واجهة برمجة التطبيقات getSandboxedSdks()
في SdkSandboxController
فئة SandboxedSdk
لكل حِزم SDK التي يتم تحميلها في "مبادرة حماية الخصوصية". يتضمّن الكائن SandboxedSdk
تفاصيل حول حزمة تطوير البرامج (SDK) وsdkInterface
يمكن للعملاء التواصل معها.
في حِزم تطوير البرامج (SDK) المتاحة ضمن "مبادرة حماية الخصوصية"، من المتوقّع أن تستخدم مقتطفات رموز مشابهة لما يلي للتواصل مع حِزم تطوير البرامج (SDK) الأخرى. لنفترض أنّ هذه التعليمة البرمجية تمت كتابتها للسماح لـ "SDK1" بالتواصل مع "SDK2"، وكلاهما محمّل من خلال التطبيق في "مبادرة حماية الخصوصية".
SdkSandboxController controller = mSdkContext
.getSystemService(SdkSandboxController.class);
List<SandboxedSdk> sandboxedSdks = controller.getSandboxedSdks();
SandboxedSdk sdk2 = sandboxedSdks.stream().filter( // The SDK it wants to
// connect to, based on SDK name or SharedLibraryInfo.
try {
IBinder binder = sdk2.getInterface();
ISdkApi sdkApi = ISdkApi.Stub.asInterface(binder);
// Call API on SDK2
message = sdkApi.getMessage();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
في المثال أعلاه، تضيف حزمة SDK1 مكتبة AIDL لحزمة SDK2 كتبعية. تحتوي حزمة تطوير البرامج (SDK) للعميل هذه على رمز Binder تم إنشاؤه من خلال AIDL. يجب أن تصدّر حزمتا تطوير البرامج (SDK) مكتبة AIDL هذه. ويتطابق ذلك مع الإجراءات المتوقّع أن تفعلها التطبيقات عند التواصل مع حِزم SDK في "مبادرة حماية الخصوصية".
ستتم إضافة دعم لطريقة يتم إنشاؤها تلقائيًا لمشاركة الواجهات بين حِزم SDK في تحديث مستقبلي.
قد تحتاج حِزم SDK في وقت التشغيل إلى التواصل مع تبعيات التطبيقات وحِزم SDK الإعلانية التي لم يتم تفعيلها بعد.
توفّر واجهة برمجة التطبيقات registerAppOwnedSdkSandboxInterface()
API في SdkSandboxManager
طريقة لتسجيل واجهاتها على النظام الأساسي إذا لم يتم تفعيل ميزة وقت التشغيل في وقت التشغيل. توفّر واجهة برمجة التطبيقات getAppOwnedSdkSandboxInterfaces()
في SdkSandboxController
واجهة برمجة التطبيقات AppOwnedSdkSandboxInterface
لجميع حِزم SDK المسجّلة والمرتبطة بشكلٍ ثابت.
يوضح المثال التالي كيفية تسجيل الواجهات لجعلها متاحة للاتصال بحزمة SDK التي يتم تفعيلها في وقت التشغيل:
// Register AppOwnedSdkSandboxInterface
mSdkSandboxManager.registerAppOwnedSdkSandboxInterface(
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_NAME, (long) APP_OWNED_SDK_VERSION, new AppOwnedSdkApi())
);
يوضح هذا المثال كيفية وسيلة التواصل باستخدام حزم SDK التي لا يتم تشغيلها في وقت التشغيل:
// Get AppOwnedSdkSandboxInterface
List<AppOwnedSdkSandboxInterface> appOwnedSdks = mSdkContext
.getSystemService(SdkSandboxController.class)
.getAppOwnedSdkSandboxInterfaces();
AppOwnedSdkSandboxInterface appOwnedSdk = appOwnedSdks.stream()
.filter(s -> s.getName().contains(APP_OWNED_SDK_NAME))
.findAny()
.get();
IAppOwnedSdkApi appOwnedSdkApi =
IAppOwnedSdkApi.Stub.asInterface(appOwnedSdk.getInterface());
message = appOwnedSdkApi.getMessage();
وبما أنّ حِزم SDK الإعلانية التي لم يتم تفعيلها في وقت التشغيل قد لا تتمكّن من التسجيل بنفسها، نقترح إنشاء حزمة SDK وسيطة تعالج التسجيل وتتضمّن حِزم SDK للشركاء أو التطبيقات كملحقات مباشرة. ينشئ الوسيط SDK هذا اتصالاً بين حزم SDK والتبعيات التي لم يتم تفعيلها في وقت التشغيل والوسيط المفعَّل في وقت التشغيل الذي يعمل كمحوّل.
دعم النشاط
لا يمكن لحِزم SDK المفعَّلة في وقت التشغيل إضافة علامة نشاط إلى ملف البيان، ولا يمكنها
بدء أنشطتها الخاصة مباشرةً. يتم توفير الوصول إلى الكائن Activity
من خلال تسجيل SdkSandboxActivityHandler
وبدء نشاط وضع الحماية:
1- تسجيل SdkSandboxActivityHandler
تسجيل مثيل لـ SdkSandboxActivityHandler
باستخدام
SdkSandboxController#registerSdkSandboxActivityHandler(SandboxedActivityHandler)
تُسجِّل واجهة برمجة التطبيقات ذلك الكائن وتعرض عنصر IBinder
الذي يحدّد
SdkSandboxActivityHandler
الذي تم تمريره.
public interface SdkSandboxActivityHandler {
void onActivityCreated(Activity activity);
}
تُسجِّل واجهة برمجة التطبيقات ذلك الكائن وتعرض عنصر IBinder
الذي يحدّد
SdkSandboxActivityHandler
الذي تم تمريره.
2- بدء نشاط "وضع الحماية"
تمرِّر حزمة تطوير البرامج (SDK) الرمز المميّز الذي تم عرضه لتحديد رمز SdkSandboxActivityHandler
المسجَّل إلى تطبيق العميل. بعد ذلك، يتصل تطبيق العميل بـ
SdkSandboxManager#startSdkSandboxActivity(Activity, Binder)
، ويمرِّر
نشاطًا لبدء وضع الحماية منه، ويحدِّد رمزًا مميزًا يحدِّد رمز SdkSandboxActivityHandler
المسجَّل.
تؤدي هذه الخطوة إلى بدء نشاط جديد على النظام الأساسي يتم تشغيله في وقت تشغيل SDK نفسه الذي يتم فيه تشغيل حزمة SDK المطلوبة.
عند بدء النشاط، يتم إرسال إشعار إلى حزمة SDK من خلال اتصال مع SdkSandboxActivityHandler#onActivityCreated(Activity)
كجزء من عملية تنفيذ Activity#OnCreate(Bundle)
.
على سبيل المثال، لديه إمكانية الوصول إلى الكائن Activity
، ويمكن للمتصل ضبط
contentView
على طريقة عرض من خلال طلب الرقم Activity#setContentView(View)
.
لتسجيل طلبات معاودة الاتصال في مراحل النشاط، استخدِم
Activity#registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks)
.
لتسجيل "OnBackInvokedCallback
" في النشاط الذي تم تمريره، استخدِم
Activity#getOnBackInvokedDispatcher().registerOnBackInvokedCallback(Int,
OnBackInvokedCallback)
.
اختبار مشغّلات الفيديو في "وقت تشغيل 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
. عدة أمثلة:
- سيؤدي الوصول إلى الملفات باستخدام إطار عمل الوصول إلى مساحة التخزين إلى عرض الخطأ
SecurityException
. - ستعرض
getExternalFilsDir()
دائمًا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
قراءة SharedPreferences
للعميل
يمكن لتطبيقات العملاء اختيار مشاركة مجموعة من المفاتيح من خلال SharedPreferences
مع SdkSandbox
. يمكن لحِزم SDK قراءة البيانات التي تمت مزامنتها من تطبيق العميل باستخدام
SdkSanboxController#getClientSharedPreferences()
API. واجهة برمجة التطبيقات SharedPreferences
التي تعرضها واجهة برمجة التطبيقات هذه مخصّصة للقراءة فقط. يجب عدم الكتابة إليه.
الوصول إلى المعرِّف الإعلاني المقدَّم من "خدمات Google Play"
إذا كانت حزمة تطوير البرامج (SDK) بحاجة إلى الوصول إلى المعرِّف الإعلاني الذي تقدِّمه "خدمات Google Play":
- يُرجى تقديم بيان عن إذن "
android.permission.ACCESS_ADSERVICES_AD_ID
" في ملف بيان حزمة تطوير البرامج (SDK). - استخدِم
AdIdManager#getAdId()
لاسترداد القيمة بشكل غير متزامن.
الوصول إلى معرّف مجموعة التطبيقات المقدَّم من "خدمات Google Play"
إذا كانت حزمة تطوير البرامج (SDK) بحاجة إلى الوصول إلى معرّف مجموعة التطبيقات الذي توفّره "خدمات Google Play":
- استخدِم
AppSetIdManager#getAppSetId()
لاسترداد القيمة بشكل غير متزامن.
تحديث تطبيقات العملاء
لطلب حزمة تطوير برامج (SDK) تعمل في وقت تشغيل SDK، عليك إجراء التغييرات التالية على تطبيق عميل الاتصال:
أضِف إذنَي
INTERNET
وACCESS_NETWORK_STATE
إلى بيان التطبيق:<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
في النشاط الذي يتضمّن إعلانًا في تطبيقك، يجب الإشارة إلى
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;
تحقَّق من توفُّر عملية تشغيل SDK على الجهاز.
تحقَّق من الثابت
SdkSandboxState
(getSdkSandboxState()
).SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION
يعني أنّ "وقت تشغيل SDK" متاح.تحقَّق من إتمام الاتصال برقم
loadSdk()
. ويكون الإجراء ناجحًا إذا لم يتم طرح أي استثناءات، والمستلِم هو مثالSandboxedSdk
.عليك طلب "
loadSdk()
" من المقدّمة. وإذا تم طلبها من الخلفية، سيتم طرحSecurityException
.تحقَّق من
OutcomeReceiver
للاطّلاع على مثيلSandboxedSdk
للتأكّد مما إذا تم إسقاطLoadSdkException
. يشير الاستثناءات إلى أنّ "وقت تشغيل SDK" قد لا يكون متاحًا.
إذا تعذَّر استدعاء
SdkSandboxState
أوloadSdk
، لن يكون "وقت تشغيل SDK" متاحًا، ومن المفترض أن يعود الاستدعاء إلى حزمة تطوير البرامج (SDK) الحالية.حدِّد فئة معاودة الاتصال من خلال تنفيذ
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()
في
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); }
يمكن للتطبيق اختيار مشاركة بعض مفاتيح
SharedPreferences
التلقائية مع وضع الحماية. ويمكنهم إجراء ذلك من خلال استدعاء طريقةSdkSandboxManager#addSyncedSharedPreferencesKeys(Set<String>keys)
على أي مثيل لمديرSdkSandbox
. بعد أن يرسِل التطبيق إشعارًا إلىSdkSandboxManager
بشأن المفاتيح التي يجب مزامنتها، سيزامنSdkSandboxManager
قيم هذه المفاتيح في وضع الحماية وأجهزة SDK، ثم يمكنه قراءتها باستخدامSdkSandboxController#getClientSharedPreferences
. راجع Reading Client's SharedPreferences للحصول على مزيد من المعلومات.لا تستمر مجموعة المفاتيح التي تتم مزامنتها خلال إعادة تشغيل التطبيق ويتم محو البيانات التي تمت مزامنتها مع وضع الحماية عند إعادة تشغيل وضع الحماية. وبالتالي، من الضروري أن يبدأ التطبيق عملية المزامنة من خلال استدعاء
addSyncedSharedPreferencesKeys
في كل مرة يتم فيها بدء تشغيل التطبيق.يمكنك تعديل مجموعة المفاتيح التي تتم مزامنتها عن طريق طلب الرمز
SdkSandboxManager#removeSyncedSharedPreferencesKeys(Set<String>keys)
لإزالة المفاتيح. لعرض المجموعة الحالية من المفاتيح التي تتم مزامنتها، استخدِمSdkSandboxManager#getSyncedSharedPreferencesKeys()
.نقترح الاحتفاظ بمجموعة المفاتيح صغيرة قدر الإمكان واستخدامها فقط عند الضرورة. إذا كنت تريد تمرير المعلومات إلى حِزم تطوير البرامج (SDK) لأغراض عامة، يُرجى التواصل مع SDK مباشرةً باستخدام واجهة
SandboxedSdk
. أحد السيناريوهات المحتملة لاستخدام واجهات برمجة التطبيقات هذه هو إذا كان التطبيق يستخدم حزمة تطوير البرامج (SDK) لمنصّة إدارة الموافقة (CMP)، ستهتم حِزم تطوير البرامج (SDK) داخل وضع الحماية بقراءة مخازن حزمة تطوير البرامج CPM SDK للبيانات تلقائيًاSharedPreferences
.Kotlin
override fun onCreate(savedInstanceState: Bundle?) { … // At some point, initiate the set of keys for synchronization with sandbox mSdkSandboxManager.addSyncedSharedPreferencesKeys(Set.of("foo", "bar")); }
Java
@Override protected void onCreate(Bundle savedInstanceState) { … // At some point, initiate the set of keys for synchronization with sandbox mSdkSandboxManager.addSyncedSharedPreferencesKeys(Set.of("foo", "bar")); }
لمعالجة حالة انتهاء عملية وضع الحماية لحزمة 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، يجب تحميل طرق العرض هذه مرة أخرى لبدء عملية وضع حماية جديدة.
أضِف عنصر الاعتماد إلى وحدة حزمة تطوير البرامج (SDK) في
build.gradle
في تطبيق العميل:dependencies { ... implementation project(':<your-sdk-module>') ... }
اختبار تطبيقاتك
لتشغيل تطبيق العميل، يجب تثبيت تطبيق SDK وتطبيق العميل على جهاز الاختبار باستخدام Android Studio أو سطر الأوامر.
النشر من خلال "استوديو Android"
عند النشر من خلال "استوديو Android"، أكمل الخطوات التالية:
- افتح مشروع "استوديو Android" لتطبيق عميلك.
- انتقِل إلى تشغيل > تعديل عمليات الضبط. تظهر نافذة إعداد التشغيل/تصحيح الأخطاء.
- ضمن خيارات الإطلاق، اضبط إطلاق على نشاط محدّد.
- انقر على قائمة الخيارات الإضافية بجانب "النشاط" واختَر النشاط الرئيسي لعميلك.
- انقر على تطبيق ثم حسنًا.
- انقر على تشغيل
لتثبيت تطبيق العميل وحزمة تطوير البرامج (SDK) على جهاز الاختبار.
النشر في سطر الأوامر
عند النشر باستخدام سطر الأوامر، أكمل الخطوات الواردة في القائمة التالية.
يفترض هذا القسم أنّ اسم وحدة تطبيق حزمة تطوير البرامج (SDK) هو sdk-app
وأنّ اسم وحدة تطبيق العميل هو client-app
.
من الوحدة الطرفية لسطر الأوامر، يمكنك إنشاء حِزم APK لحزمة تطوير البرامج (SDK) ضمن "مبادرة حماية الخصوصية":
./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
يؤدي هذا إلى عرض الموقع الجغرافي لملفات APK التي تم إنشاؤها. تم توقيع حِزم APK هذه باستخدام مفتاح تصحيح الأخطاء المحلي. ستحتاج إلى هذا المسار في الأمر التالي.
تثبيت حزمة APK على جهازك:
adb install -t /path/to/your/standalone.apk
في "استوديو Android"، انقر على تشغيل > تعديل عمليات الضبط. ستظهر نافذة التشغيل/تصحيح الأخطاء.
ضمن خيارات التثبيت، اضبط نشر على حزمة APK التلقائية.
انقر على تطبيق ثم حسنًا.
انقر على Run (تشغيل) لتثبيت حزمة APK على جهاز الاختبار.
تصحيح أخطاء تطبيقاتك
لتصحيح أخطاء تطبيق العميل، انقر على الزر تصحيح الأخطاء
في "استوديو Android".
لتصحيح أخطاء تطبيق SDK، انتقل إلى Run > Attach to Process (تشغيل > إرفاق بالعملية)، حيث تظهر لك شاشة منبثقة (كما هو موضح أدناه). ضَع علامة في المربّع عرض جميع العمليات. في القائمة التي تظهر،
ابحث عن عملية تسمى CLIENT_APP_PROCESS_sdk_sandbox
. حدد هذا الخيار وأضف نقاط التوقف في التعليمات البرمجية لتطبيق SDK
لبدء تصحيح أخطاء SDK.
![تظهر عملية تطبيق حزمة تطوير البرامج (SDK) في عرض على شكل قائمة بالقرب من أسفل مربّع الحوار.](https://developers.google.cn/static/privacy-sandbox/assets/images/android/debug-sdk-app.png?authuser=8&hl=ar)
بدء وقت تشغيل حزمة 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) التي يتم تحميلها حاليًا
يمكنك الاطّلاع على حِزم تطوير البرامج (SDK) التي يتم تحميلها حاليًا، وذلك باستخدام وظيفة getSandboxedSdks
داخل SdkSandboxManager
.
القيود
للحصول على قائمة بالإمكانات قيد التقدم في "وقت تشغيل SDK"، يمكنك الاطّلاع على ملاحظات الإصدار.
عيّنات تعليمات برمجية
يحتوي مستودع واجهات برمجة التطبيقات (API) الخاصة بـ "وقت تشغيل SDK" و"الحفاظ على الخصوصية" على GitHub على مجموعة من المشاريع الفردية الخاصة بـ "استوديو Android" لمساعدتك في البدء، بما في ذلك نماذج توضّح كيفية إعداد "وقت تشغيل SDK" واستدعاءه.الإبلاغ عن الأخطاء والمشاكل
ملاحظاتك هي جزء مهم من "مبادرة حماية الخصوصية" على Android. يُرجى إعلامنا بأي مشاكل تصادفها أو أفكارًا لتحسين "مبادرة حماية الخصوصية" على Android.
محتوى مُقترَح لك
- ملاحظة: يتم عرض نص الرابط عند إيقاف JavaScript.
- وقت تشغيل حزمة تطوير البرامج (SDK)
- ملاحظات الإصدار
- Protected Audience API على دليل مطوّري برامج Android