המדריך למפתחים של זמן ריצה ל-SDK

בהמשך לקריאה על ארגז החול לפרטיות ב-Android תיעוד, השתמשו בלחצן תצוגה מקדימה למפתחים או בטא כדי לבחור בגרסת התוכנה שאיתה אתם עובדים, כי ההוראות עשויות להשתנות.


שליחת משוב

זמן הריצה של ה-SDK מאפשר לערכות ה-SDK לפעול בארגז חול (Sandbox) ייעודי בנפרד את אפליקציית השיחות. זמן הריצה ל-SDK מספק אמצעי הגנה ואחריות משופרים סביב איסוף נתוני משתמשים. הפעולה הזו מתבצעת באמצעות שינוי הרצה שמגבילה את הרשאות הגישה לנתונים ואת קבוצת ההרשאות המותרות. מידע נוסף על זמן הריצה ל-SDK זמין בהצעה לעיצוב.

השלבים בדף הזה מנחים אתכם בתהליך היצירה של ערכת SDK שתואמת לזמן ריצה, שמגדירה תצוגה מבוססת-אינטרנט שניתן לעבד מרחוק. לאפליקציית שיחות.

מגבלות ידועות

לרשימת היכולות בתהליך פיתוח בשביל זמן הריצה של ה-SDK, אפשר לעיין בגרסה הערות.

ההגבלות הבאות צפויות שיתוקנו במכשירי Android העיקריים הבאים מהדורת פלטפורמה.

  • רינדור מודעה בתצוגה שניתן לגלול. לדוגמה, RecyclerView לא פועלות כראוי.
    • ייתכנו בעיות בשינוי הגודל.
    • אירועי גלילה במגע של המשתמש לא מועברים כראוי לסביבת זמן הריצה.
  • ממשק API לאחסון

הבעיה הבאה תיפתר בשנת 2023:

  • ממשקי ה-API getAdId ו-getAppSetId עדיין לא פועלים כראוי מאז התמיכה של השפות האלה טרם הופעלו.

לפני שמתחילים

לפני שמתחילים, צריך לבצע את השלבים הבאים:

  1. להגדיר את סביבת הפיתוח לארגז החול לפרטיות ב- Android. כלים שתומכים בזמן הריצה של ה-SDK נמצאים בפיתוח פעיל, לכן במדריך זה תידרשו להשתמש בגרסה העדכנית ביותר של Android Canary Studio. אפשר להפעיל את הגרסה הזו של Android Studio במקביל לגרסה אחרת הגרסאות שבהן השתמשת, לכן עליך להודיע לנו אם הדרישה הזו לא עובד בשבילכם.

  2. מתקינים תמונת מערכת במכשיר נתמך או להגדיר אמולטור שכולל תמיכה בארגז החול לפרטיות ב- Android.

הגדרת הפרויקט ב-Android Studio

כדי לנסות את זמן הריצה של ה-SDK, צריך להשתמש במודל שדומה client-server model. ההבדל העיקרי הוא שאפליקציות לקוח) וערכות SDK ("השרת") פועלות באותו מכשיר.

  1. מוסיפים מודול אפליקציה לפרויקט. המודול הזה משמש כלקוח שעומד בבסיס ה-SDK.
  2. במודול האפליקציה, מפעילים את זמן הריצה של ה-SDK. להצהיר על ההרשאות הנדרשות להגדיר שירותי מודעות שספציפיים ל-API.
  3. מוסיפים לפרויקט מודול אחד של ספרייה. המודול הזה מכיל את קוד ה-SDK שלך.
  4. אתם צריכים להצהיר על ההרשאות הדרושות במודול ה-SDK. לא צריך להגדיר שירותי מודעות ספציפיים לממשק API במודול הזה.
  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 (Extension Level 4) ויצירה אמולטור עם התמונה הזו שכולל את חנות Play.

תלוי אם אתם מפתחי SDK או מפתחי אפליקציות, ההגדרה הסופית שונה מההגדרה שתואר בפסקה הקודמת.

מתקינים את ה-SDK במכשיר בדיקה, בדומה לאופן שבו מתקינים אפליקציה, באמצעות את Android Studio או Android Debug Bridge (ADB). כדי לעזור לך להתחיל, יצרנו אפליקציות לדוגמה ב-Kotlin וב-Java את שפות התכנות האלה, מאגר GitHub. קובצי ה-README וקובצי המניפסט כוללים הערות שמתארות מה צריך לשנות כדי להריץ את הדוגמה בגרסאות יציבות של Android Studio.

הכנת ה-SDK

  1. יצירת ספרייה ברמת המודול באופן ידני. הוא משמש כ-wrapper את קוד ההטמעה כדי לבנות את ה-APK של ה-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 תעבור הידור (compile), צריך לשנות את השיטות לטיפול ב-SDK מחזור חיים:

onLoadSdk()

טוענת את ה-SDK ל-Sandbox, ושולחת הודעה לאפליקציית הקריאה כשה-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;
        }
    }

שימוש בממשקי API לאחסון ב-SDK

ערכות SDK בזמן הריצה של ה-SDK לא יכולות יותר לגשת, לקרוא או לכתוב אחסון פנימי ולהפך. לזמן הריצה של ה-SDK יוקצה אזור אחסון פנימי משלו, כלומר להיות נפרדות מהאפליקציה.

ערכות SDK יוכלו לגשת לאחסון הפנימי הנפרד הזה באמצעות הקובץ storage API באובייקט Context שהוחזר על-ידי SandboxedSdkProvider#getContext(). ערכות SDK יכולות להשתמש רק באחסון פנימי, לכן ממשקי API של אחסון פנימי, כמו Context.getFilesDir() או Context.getCacheDir() יפעל. הצגת דוגמאות נוספות ב גישה מאחסון פנימי.

אין תמיכה בגישה לאחסון חיצוני מזמן הריצה של ה-SDK. קריאה לממשקי API גישה לאחסון חיצוני תגרור חריגה או תחזיר null. כמה דוגמאות:

ב-Android 13, כל ערכות ה-SDK בזמן הריצה של ה-SDK יחלקו את האחסון הפנימי שהוקצה לזמן הריצה של ה-SDK. האחסון יישמר עד שאפליקציית הלקוח הוסרה, או כשמנקים את הנתונים של אפליקציית הלקוח.

עליך להשתמש בContext שהוחזר על ידי SandboxedSdkProvider.getContext() ל אחסון. שימוש ב-File Storage API בכל מופע של אובייקט 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, רק API אחד מחזיר נתיב לאחסון לכל SDK: Context#getDataDir()

ב-Android 14, כל ממשקי ה-API של האחסון הפנימי באובייקט Context מחזירים נתיב האחסון של כל SDK. יכול להיות שתצטרכו להפעיל את התכונה הזו על ידי הרצת פקודת adb הבאה:

adb shell device_config put adservices sdksandbox_customized_sdk_context_enabled true

גישה למזהה הפרסום שסופק על ידי Google Play Services

אם ל-SDK נדרשת גישה למזהה הפרסום שסופק על ידי Google Play Services:

  • צריך להצהיר על ההרשאה android.permission.ACCESS_ADSERVICES_AD_ID במניפסט של ה-SDK.
  • כדי לאחזר את הערך באופן אסינכרוני, משתמשים ב-AdIdManager#getAdId().

גישה למזהה קבוצת האפליקציות שסופק על ידי Google Play Services

אם ל-SDK נדרשת גישה למזהה קבוצת האפליקציות שסופק על ידי Google Play Services:

  • כדי לאחזר את הערך באופן אסינכרוני, משתמשים ב-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. להגדיר מחלקה של קריאה חוזרת (callback) באמצעות הטמעה של OutcomeReceiver כדי לבצע אינטראקציה עם ה-SDK בזמן הריצה אחרי שהוא נטען. בתוך לדוגמה, הלקוח משתמש בקריאה חוזרת (callback) כדי להמתין עד שה-SDK נטען לאחר מכן ינסה לעבד תצוגת אינטרנט מה-SDK. הקריאות החוזרות (callback) יוגדרו בהמשך השלב הזה.

    Kotlin

        private inner class LoadSdkOutcomeReceiverImpl private constructor() :
                OutcomeReceiver<SandboxedSdk?, LoadSdkException?> {
    
          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<LoadSdkResponse, LoadSdkException> {
            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<Bundle, RequestSurfacePackageException> {
            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<Bundle, RequestSurfacePackageException> {
            @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, את הקריאות החוזרות (callback) הנדרשות, שולחים בקשה לטעינת ה-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. כדי לטפל בבעיה כשתהליך ה-Sandbox של ה-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);
              }
          }
    

    כדי לרשום את הקריאה החוזרת הזו לקבלת מידע על מועד ה-Sandbox של ה-SDK הסתיים, הוסף את השורה הבאה בכל עת:

    Kotlin

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

    Java

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

    מאחר שהמצב של ארגז החול אובד כשהתהליך מסתיים, הצפיות יכול להיות שערכת ה-SDK מעבדת מרחוק את הנתונים שלה. כדי להמשיך את האינטראקציה עם ערכות ה-SDK, צריך לטעון את התצוגות האלה שוב מתחיל תהליך חדש של Sandbox.

  7. צריך להוסיף תלות במודול ה-SDK לאפליקציית הלקוח build.gradle:

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

בדיקת האפליקציות

כדי להריץ את אפליקציית הלקוח, צריך להתקין את אפליקציית ה-SDK ואת אפליקציית הלקוח במכשיר הבדיקה באמצעות Android Studio או שורת הפקודה.

פריסה דרך Android Studio

כשפורסים דרך Android Studio, מבצעים את השלבים הבאים:

  1. פותחים את פרויקט Android Studio של אפליקציית הלקוח.
  2. עוברים אל Run > עורכים את ההגדרות האישיות. החלון Run/Debug Configuration מופיעה.
  3. בקטע Launch options (אפשרויות הפעלה), מגדירים את Launch (הפעלה) כ-Specified Activity (פעילות ספציפית).
  4. לוחצים על תפריט שלוש הנקודות שלצד 'פעילות' ובוחרים באפשרות פעילות ראשית. עבור הלקוח שלך.
  5. לוחצים על אישור ואז על אישור.
  6. לוחצים על הפעלה. כדי להתקין את אפליקציית הלקוח ואת ה-SDK במכשיר הבדיקה.

פריסה בשורת הפקודה

בפריסה באמצעות שורת הפקודה, צריך להשלים את השלבים ברשימה הבאה. הקטע הזה מניח שהשם של מודול ה-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 Studio, לוחצים על הפעלה > עורכים את ההגדרות האישיות. הכלי הפעלה/ניפוי באגים חלון מופיע.

  4. בקטע אפשרויות התקנה, מגדירים את פריסה לערך APK ברירת המחדל.

  5. לוחצים על אישור ואז על אישור.

  6. לוחצים על Run כדי להתקין את חבילת ה-APK במכשיר הבדיקה.

ניפוי באגים באפליקציות

כדי לנפות באגים באפליקציית הלקוח, לוחצים על ניפוי באגים. ב-Android Studio.

כדי לנפות באגים באפליקציית ה-SDK, עוברים אל הפעלה > צירוף לתהליך, שבו מוצג חלון קופץ (מוצג למטה). מסמנים את התיבה הצגת כל התהליכים. ברשימה מופיע, מחפשים תהליך בשם CLIENT_APP_PROCESS_sdk_sandbox. יש לבחור באפשרות הזו ולהוסיף נקודות עצירה בקוד של אפליקציית ה-SDK כדי אפשר להתחיל לנפות באגים ב-SDK.

תהליך אפליקציית ה-SDK מופיע בתצוגת רשימה בחלק התחתון של המסך
  של תיבת הדו-שיח
במסך בחירת תהליך, שבו אפשר צריך לבחור את אפליקציית ה-SDK לניפוי באגים.

איך מפעילים ומפסיקים את זמן הריצה של ה-SDK משורת הפקודה

כדי להתחיל את תהליך זמן הריצה של ה-SDK לאפליקציה, צריך להשתמש בפקודת המעטפת הבאה:

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 Studio שיעזרו לך לקבל שהתחילו, כולל דוגמאות שממחישות איך להפעיל את ה-SDK ולקרוא לו זמן ריצה.

דיווח על באגים ובעיות

המשוב שלכם הוא חלק חיוני בארגז החול לפרטיות ב-Android. נשמח לשמוע ממך על כל בעיות שמצאתם או רעיונות לשיפור ארגז החול לפרטיות ב-Android.