המדריך למפתחים של 'אותות אפליקציות מוגנים'

כדי לעזור למפתחים להתחיל להתנסות ב-Protected App Signals API, במאמר הזה מתוארים כל ממשקי ה-API שבפלטפורמת ה-API, איך מגדירים סביבת בדיקה ודוגמאות להגדרות ולסקריפטים.

היסטוריית גרסאות

ינואר 2024

גרסה ראשונה של המדריך למפתחים שתומך בגרסה MVP של PAS

מרץ 2024

שינויים ב-API לתמיכה בגרסת M-2024-05 של Android API ובגרסת אפריל 2024 של הרכיבים בצד השרת. השינויים הבולטים ביותר:

  • נוספו פרטים על ההרשאות הנדרשות ל-API במכשיר
  • נוספו פרטים לגבי ניהול מכסת האותות במכשיר
  • חתימת generateBid מעודכנת עם שינויים שקשורים לתמיכה לפי הקשר באחזור מודעות ובתעבורת נתונים יוצאת
  • מסמכי תיעוד מעודכנים של reportWin, כולל תמיכה בתעבורת נתונים יוצאת (egress)
  • עדכון מסמכי התיעוד של ה-API לאחזור מודעות, שבהם מסירים את התמיכה באחזור מודעות באמצעות BYOS ומתעדים את ה-UDF לאחזור מודעות

סקירה כללית על ממשקי API

הפלטפורמה של Protected Signals API כוללת קבוצות משנה שונות של API במערכות שונות:

  • ממשקי API ל-Android:
    • ממשק ה-API לאיסוף אותות, שמורכב מה:
    • עדכון של Signals API
    • ממשק API של קידוד אותות
    • Protected Auction Support API: לשימוש ב-SDK כדי להריץ את המכרז המוגן בשרתים של Bidding ומכרז (B&A) באמצעות Protected App Signals.
  • ממשקי API בצד השרת:
    • Protected Auction API: סדרה של סקריפטים של JS שפועלים בשרתי הבידינג ובשרתי המכרזים. ה-API הזה מאפשר למפיצים ולקונים לכתוב את הלוגיקה ליישום המכרז המוגן.
    • ממשק API לאחזור מודעות: אחראי על מתן רשימה של מודעות מועמדים, בהינתן מידע ההקשרי ופרטי המשתמש שזמינים לשרת הבידינג של הקונה.

לקוח Android

בצד הלקוח, הפלטפורמה 'Protected App Signals' כוללת שלושה ממשקי API שונים:

  • עדכון אותות: Android System API שמאפשר איסוף של אותות במכשיר.
  • קידוד אותות: ממשק API של JavaScript שנועד להכין את האותות שיישלחו לשרת במהלך המכרז.
  • Protected Auction Support: API לתמיכה בביצוע של מכרזים מוגנים בשרתי הבידינג ובשרתי מכרזים. ה-API הזה לא ספציפי ל-Protected App Signals ומשמש גם לתמיכה במכרזים של Protected Audience API.

עדכון של Signals API

ה-Update Signals API מאפשר לטכנולוגיות פרסום לרשום אותות שקשורים למשתמשים ולאפליקציות בשם הקונה. ה-API פועל לפי מודל של הענקת גישה. מבצע הקריאה החוזרת מספק URI שממנו ה-framework מאחזרת את האותות התואמים ואת הלוגיקה לקידוד האותות האלה לשימוש במכרז.

ל-API נדרשת ההרשאה android.permission.ACCESS_ADSERVICES_PROTECTED_SIGNALS.

ה-API updateSignals() יאחזר מה-URI אובייקט JSON, שמתאר אילו אותות להוסיף או להסיר, ואיך להכין את האותות האלה למכרז.

Executor executor = Executors.newCachedThreadPool();
ProtectedSignalsManager protectedSignalsManager
     =  ProtectedSignalsManager.get(context);

// Initialize a UpdateSignalsRequest
UpdateSignalsRequest updateSignalsRequest = new
  UpdateSignalsRequest.Builder(Uri.parse("https://example-adtech1.com/signals"))
      .build();

OutcomeReceiver<Object, Exception> outcomeReceiver = new OutcomeReceiver<Object, Exception>() {
  @Override
  public void onResult(Object o) {
    //Post-success actions
  }

  @Override
  public void onError(Exception error) {
    //Post-failure actions
  };

// Call updateSignals
protectedSignalsManager.updateSignals(updateSignalsRequest,
    executor,
    outcomeReceiver);

הפלטפורמה שולחת בקשת https ל-URI שצוין בבקשה לאחזור עדכוני האות. יחד עם עדכוני האות, התגובה יכולה לכלול נקודת קצה שמארחת את לוגיקת הקידוד להמרת האותות הגולמיים למטען ייעודי (payload) מקודד. עדכוני האותות צפויים להיות בפורמט JSON ויכולים לכלול את המפתחות הבאים:

המפתחות ברמה העליונה של אובייקט ה-JSON חייבים להתאים לאחת מחמש פקודות:

מפתח

תיאור

put

הוספת אות חדש והחלפת האותות הקיימים בעלי אותו המפתח. הערך

מדובר באובייקט JSON שבו המפתחות הם מחרוזות בסיס 64 שתואמות למפתח שעבורו רוצים להזין. הערכים הם מחרוזת בסיס 64 שתואמת לערך שיש לשים.

append

צירוף של אותות/אותות חדשים לסדרת זמנים של אותות, תוך הסרת האותות הישנים ביותר

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

'values': רשימה של מחרוזות בסיס 64 שתואמות לערכי האותות שיש לצרף לסדרת הזמנים.

"maxSignals": מספר הערכים המקסימלי שמותרים בסדרת הזמנים הזו. אם

מספר האותות שמשויכים למפתח הנוכחי חורג מ-maxSignals, האותות הישנים ביותר יוסרו. לתשומת ליבכם: אפשר לצרף למפתח שנוסף באמצעות פוט. שימו לב שצירוף של ערכים רבים יותר ממספר הערכים המקסימלי יגרום לכשל.

put_if_not_present

הוספת אות חדש רק אם אין אותות קיימים עם אותו מַפְתח. הערך לערך הזה הוא אובייקט JSON שבו המפתחות הם מחרוזות בסיס 64 שתואמות למפתח שעבורו רוצים להזין. הערכים הם מחרוזת בסיס 64 שתואמת לערך שיש לשים.

remove

הסרת האות של מפתח מסוים. הערך הזה הוא רשימה של מחרוזות בסיס 64 שתואמות למפתחות של האותות שצריך למחוק.

update_encoder

מספק פעולה לעדכון נקודת הקצה, ו-URI שאפשר להשתמש בו

כדי לאחזר את לוגיקת הקידוד. מפתח המשנה לשליחת פעולת עדכון הוא 'פעולה'

הערכים הנתמכים כרגע הם רק "REGISTER" (הרשמה) כדי לרשום את נקודת הקצה של המקודד אם היא סופקה בפעם הראשונה, או להחליף את נקודת הקצה הקיימת בנקודת הקצה החדשה שסופקה. חובה לציין את נקודת הקצה בפעולה REGISTER.מפתח המשנה לציון נקודת קצה (endpoint) של המקודד הוא endpoint והערך הוא ה-URI

של נקודת הקצה.

בקשת JSON תיראה כך:

{
    "put": {
        "AAAAAQ==": "AAAAZQ==",
        "AAAAAg==": "AAAAZg=="
    },
    "append": {
        "AAAAAw==": {
            "values": [
                "AAAAZw=="
            ],
            "max_signals": 3
        }
    },
    "put_if_not_present": {
        "AAAABA==": "AAAAaQ==",
        "AAAABQ==": "AAAAag=="
    },
    "update_encoder": {
        "action": "REGISTER",
        "endpoint": "https://adtech1.com/Protected App Signals_encode_script.js"
    }
}

המכסה של האותות במכשיר היא 10Kb עד 15Kb. אם תהיה חריגה מהמכסה, PPAPI יוציא אותות באמצעות אסטרטגיית FIFO. תהליך הפינוי יאפשר חריגה קלה מהמכסה לפרקי זמן קצרים, על מנת לצמצם את התדירות של הפינוי.

ממשק API של קידוד אותות

הקונים נדרשים לספק פונקציית Java Script שתשמש לקידוד האותות שמאוחסנים במכשיר לצורך שליחה לשרת במהלך המכרז המוגן. הקונים יכולים לספק את הסקריפט הזה על ידי הוספת כתובת ה-URL שממנה ניתן לאחזר אותו באמצעות המפתח "update_encoder", בכל אחת מהתגובות לבקשת API של UpdateSignal API. הסקריפט יכלול את החתימה הבאה:

function encodeSignals(signals, maxSize) {
  let result = new Uint8Array(maxSize);
  // first entry will contain the total size
  let size = 1;
  let keys = 0;
  
  for (const [key, values] of signals.entries()) {
    keys++;
    // In this encoding we only care about the first byte
    console.log("key " + keys + " is " + key)
    result[size++] = key[0];
    result[size++] = values.length;
    for(const value of values) {
      result[size++] = value.signal_value[0];
    }
  }
  result[0] = keys;
  
  return { 'status': 0, 'results': result.subarray(0, size)};
}

הפרמטר signals הוא מפה ממפתחות בצורת UInt8Arrays בגודל 4 לרשימות של אובייקטים מוגנות של אפליקציות. לכל אובייקט של 'אותות של אפליקציות מוגנות' יש שלושה שדות:

  • signal_value: ממשק UInt8Array שמייצג את ערך האות.
  • creation_time: מספר שמייצג את זמן היצירה של האותות, בשניות של זמן מערכת.
  • package_name: מחרוזת שמייצגת את שם החבילה שיצרה את האות.

הפרמטר maxSize הוא מספר שמתאר את גודל המערך הגדול ביותר שמותר לפלט.

הפונקציה צריכה להפיק פלט של אובייקט עם שני שדות:

  • status: הוא צריך להיות 0 אם הסקריפט הופעל בהצלחה.
  • results: צריך להיות ממשק UInt8Array שהאורך שלו קטן מ-maxSize או שווה לו. המערך הזה יישלח לשרת במהלך מכירות פומביות, והוכן על ידי הסקריפט prepareDataForAdRetrieval.

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

קידוד אותות

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

adb shell cmd jobscheduler run -f com.google.android.adservices.api 29
ניהול גרסאות לוגי של מקודד

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

Response header for providing encoder version : X_ENCODER_VERSION

Protected Auction Support API

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

שירותי בידינג ושירותי מכרזים

ממשקי ה-API בצד השרת כוללים:

  • Protected Auction API: סדרה של פונקציות JS או UDFs שקונים ומפיצים יכולים לפרוס ברכיבי ה-B&A שבבעלותם, כדי לקבוע את הצעות המחיר ואת לוגיקת המכרז.
  • API לאחזור מודעות: הקונים יכולים להטמיע את ה-API הזה על ידי הטמעת נקודת קצה ל-REST שתהיה אחראית על אספקת קבוצת המודעות המועמדות להשתתפות במכרז של אות אפליקציה מוגנת.

Protected Auction API

Protected Auction API כולל מ-JS API או מ-UDF שהקונים והמוכרים יכולים להשתמש בהם כדי ליישם את הלוגיקה של המכרז והבידינג.

UDFs לטכנולוגיית מודעות של קונים
הכנהDataForAdRetrieval UDF

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

// Inputs
// ------
// encodedOnDeviceSignals: A Uint8Array of bytes from the device.
// encodedOnDeviceSignalsVersion: An integer representing the encoded
//   version of the signals.
// sellerAuctionSignals: Information about auction (ad format, size) derived
//                       contextually.
// contextualSignals: Additional contextual signals that could help in
//                    generating bids.
//
// Outputs
// -------
// Returns a JSON structure to be used for retrieval.
// The structure of this object is left to the adtech.
function prepareDataForAdRetrieval(encodedOnDeviceSignals,encodedOnDeviceSignalsVersion,sellerAuctionSignals,contextualSignals) {
   return {};
}
יצירת הצעת מחיר מסוג UDF

אחרי החזרת K המודעות האפשריות המובילות, המודעות הפוטנציאליות יועברו ללוגיקת הבידינג בהתאמה אישית של הקונה, generateBid UDF:

// Inputs
// ------
// ads: Data string returned by the ads retrieval service. This can include Protected App Signals
//   ads and related ads metadata.
// sellerAuctionSignals: Information about the auction (ad format, size),
//                       derived contextually
// buyerSignals: Any additional contextual information provided by the buyer
// preprocessedDataForRetrieval: This is the output of this UDF.
function generateBid(ads, sellerAuctionSignals, buyerSignals,
                    preprocessedDataForRetrieval,
                    rawSignals, rawSignalsVersion) {
    return { "ad": <ad Value Object>,
             "bid": <float>,
             "render": <render URL string>,
             'adCost': <optional float ad cost>,
             "egressPayload": <limitedEgressPayload>,
             "temporaryUnlimitedEgressPayload": <temporaryUnlimitedEgressPayload>
    };
}

הפלט של הפונקציה הזו הוא הצעת מחיר יחידה על מועמד למודעה, מיוצגת כ-JSON שווה ערך ל-ProtectedAppSignalsAdWithBidMetadata. הפונקציה יכולה גם להחזיר שני מערכים שיועברו אל reportWin כדי להפעיל אימון מודלים (פרטים נוספים על תעבורת נתונים יוצאת (egress) ואימון מודלים זמינים בסעיף הדיווח בהסבר של PAS)

פונקציית reportWin UDF

כשהמכרז מסתיים, שירות המכרזים יפיק בשביל הקונים כתובות URL לדיווח, וירשום איתותי Bluetooth באמצעות UDF (זו אותה פונקציית reportWin שמשמשת ל'קהלים מוגנים').reportWin המכשיר יצלצל אליו אחרי שהמודעה תעובד על ידי הלקוח. החתימה של השיטה הזו כמעט זהה לגרסה של Protected Audience, מלבד שני פרמטרים נוספים egressPayload ו-temporaryUnlimitedEgressPayload שמשמשים להפעלת אימון מודלים ומאוכלסים בתוצאות מ-generateBid.

// Inputs / Outputs
// ----------------
// See detailed documentation here.
function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                   buyerReportingSignals,
                   egressPayload, temporaryUnlimitedEgressPayload) {
  // ...
}
UDFs לטכנולוגיית המודעות של בית העסק
ציון UDF

ה-UDF הזה משמש את המוכרים כדי לבחור אילו מהמודעות שיתקבלו מהקונים יזכו במכרז.

function scoreAd(adMetadata, bid, auctionConfig,
                 trustedScoringSignals, bid_metadata) {
  // ...
  return {desirability: desirabilityScoreForThisAd,
              allowComponentAuction: true_or_false};
}
תוצאת דוח UDF

ה-UDF מאפשר למוכר (בסופו של דבר) לדווח ברמת האירוע עם המידע לגבי המודעה הזוכה.

function reportResult(auctionConfig, reporting_metadata) {
  // ...
  registerAdBeacon({"click", clickUrl,"view", viewUrl});
  sendReportTo(reportResultUrl);
  return signalsForWinner;
}

ממשק API לאחזור מודעות

בגרסת ה-MVP, שירות אחזור המודעות יהיה שירות מתארח שמנוהל על ידי הקונה, ושירות הבידינג מאחזר את המועמדים של המודעות מהשירות הזה. החל מאפריל 2024, השרת לאחזור המודעות צריך לפעול בסביבת ביצוע מהימנה (TEE) ולחשוף ממשק של GRPC/Proto. חברות טכנולוגיות הפרסום חייבות להגדיר את השרת הזה ולספק את כתובת ה-URL שלו כחלק מהפריסה של סטאק ה-B&A. הטמעת השירות הזה בסביבת TEE זמינה ב-GitHub של ארגז החול לפרטיות, ובשאר מסמכי התיעוד אנחנו מניחים שזה הקוד שנעשה בו שימוש בפריסה.

החל מאפריל 2024, גרסאות של B&A תומכות באחזור מודעות לפי הקשר של נתיבים. במקרה כזה, שרת ה-Open Bidding יקבל רשימה של מזהי מודעות שיישלחו על ידי שרת ה-RTB במהלך החלק ההקשרי של המכרז. המזהים יישלחו אל שרת TEE KV כדי לאחזר את כל המידע שקשור למודעות שבו ייעשה שימוש בשלב הבידינג (לדוגמה, כתובת URL של עיבוד מודעה, מטא-נתונים והטמעות של מודעות שישמשו בבחירה מהדירוג הגבוה ביותר). לא צריך לוגיקה ספציפית לפריסה של הנתיב השני, ולכן נתעד כאן רק את אופן ההגדרה של תרחיש לדוגמה לאחזור מודעות שמבוסס על סביבת TEE.

HandleRequest UDF
function HandleRequest(requestMetadata, preparedDataForAdRetrieval,
                      deviceMetadata, contextualSignals) {
    return adsMetadataString;
}

כאשר:

  • requestMetadata: JSON. מטא-נתונים של שרת לפי בקשה ל-UDF. ריקון בינתיים.
  • preparedDataForAdRetrieval: התוכן בשדה הזה תלוי באסטרטגיה לאחזור המודעות. במקרה של אחזור מודעות לפי הקשר, הפרמטר הזה יכיל את האותות הגולמיים שמקורם במכשיר ויועברו משירות הבידינג. במקרה של אחזור מודעות בסביבת TEE באמצעות שרת אחזור מודעות, הפרמטר הזה יכיל את התוצאה של ה-UDF prepareDataForAdRetrieval. הערה: בשלב הזה, ה-Protected App Signals יהיה מפוענח ולא מוצפן.
  • deviceMetadata: אובייקט JSON שמכיל מטא-נתונים של המכשיר שהועברו על ידי שירות הפרסום של המוכר. פרטים נוספים זמינים במסמכים בנושא B&A.
    • X-Accept-Language: שפת המכשיר.
    • X-User-Agent: סוכן המשתמש שבשימוש במכשיר.
    • X-BnA-Client-IP: כתובת ה-IP של המכשיר.
  • contextualSignals: מחרוזת שרירותית מגיעה משרת הבידינג לפי הקשר שמופעל על ידי אותו DSP. ה-UDF אמור להיות מסוגל לפענח את המחרוזת ולהשתמש בה. אותות לפי הקשר עשויים להכיל מידע כלשהו, כמו פרטי הגרסה של מודל למידת מכונה (ML) עבור ההטמעה המוגנת שמועברת באמצעות Protected App Signals.

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

הכנת המערכת לפיתוח

Android

כדי להגדיר את סביבת הפיתוח של Android, צריך לבצע את הפעולות הבאות:

  1. יש ליצור אמולטור (מועדף) או מכשיר פיזי שבו פועלת התמונה של 'תצוגה מקדימה למפתחים 10'
  2. מריצים את הפקודה הבאה:
adb shell am start -n com.google.android.adservices.api/com.android.adservices.ui.settings.activities.AdServicesSettingsMainActivity

לאחר מכן בוחרים באפשרות שמוצגת כדי להביע הסכמה למודעות שהאפליקציות מציעות.

  1. מריצים את הפקודה הבאה כדי להפעיל את ממשקי ה-API הרלוונטיים. יכול להיות שיהיה צורך להפעיל שוב את התכונה הזו מדי פעם, כי הגדרות ברירת המחדל של המושבת יסונכרנו מדי פעם.
adb shell device_config put adservices fledge_custom_audience_service_kill_switch false;  adb shell device_config put adservices fledge_select_ads_kill_switch false; adb shell device_config put adservices fledge_on_device_auction_kill_switch false; adb shell device_config put adservices fledge_auction_server_kill_switch false; adb shell "device_config put adservices disable_fledge_enrollment_check true";  adb shell device_config put adservices ppapi_app_allow_list '\*'; adb shell device_config put adservices fledge_auction_server_overall_timeout_ms 60000;
  1. מפעילים מחדש את המכשיר.
  2. לעקוף את מפתחות המכרז של המכשיר כדי להפנות אל שרת מפתחות המכרז. חשוב לבצע את השלב הזה לפני שמנסים להפעיל מכרז, כדי למנוע שמירה במטמון של מפתחות שגויים.

שירותי בידינג ושירותי מכרזים

כדי להגדיר את השרתים של הB&A, עיינו במסמכי התיעוד בנושא הגדרה בניהול עצמי.

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

דרישות מוקדמות

לפני שפורסים סטאק שירות של B&A, טכנולוגיות הפרסום של הקונים צריכים:

  • לוודא שהם פרסו שירות משלהם לאחזור מודעות TEE (מידע נוסף מופיע בקטע הרלוונטי).
  • חשוב לוודא שלטכנולוגיות הפרסום יש את כל ה-UDFs הנדרשים (prepareDataForAdRetrieval, generateBid, reportWin, HandleRequest) מוגדרים ומתארחים.

הבנה של האופן שבו מכרז מוגן עם Protected Audience פועלת עם B&A תעזור, אבל לא חובה.

הגדרות Terraform

כדי להשתמש באותות מוגנות של אפליקציות, טכנולוגיות הפרסום צריכות:

  • הפעלת התמיכה ב-Protected App Signals בקטע 'B&A'.
  • יש לציין נקודות קצה של כתובות URL שמהן אפשר לאחזר את ה-UDFs החדשים עבור prepareDataForAdRetrieval, generateBid ו-reportWin.

בנוסף, המדריך מניח שטכנולוגיות פרסום שרוצות להשתמש בתכונות 'B&A' לצורך רימרקטינג ימשיכו להגדיר כרגיל את כל הסימונים הקיימים של הגדרות אישיות למכרז של רימרקטינג.

הגדרות טכנולוגיות הפרסום של הקונים

על סמך קובץ ההדגמה הזה כדוגמה, הקונים צריכים להגדיר את הדגלים הבאים:

  • הפעלת Protected App Signals: ההגדרה הזו מופעלת כדי לאסוף נתונים מוגנים של אותות מאפליקציות.
  • כתובות URL מוגנות של אותות אפליקציות: כתובות ה-URL של שרתי האותות של אפליקציות מוגנות.

טכנולוגיות הפרסום צריכות להחליף את כתובות ה-URL הנכונות ב-placeholders בשדות הבאים:

module "buyer" {
  # ... More config here.

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"
    PROTECTED_APP_SIGNALS_GENERATE_BID_TIMEOUT_MS = "60000"
    TEE_AD_RETRIEVAL_KV_SERVER_ADDR               = "<service mesh address of the instance>"
    AD_RETRIEVAL_TIMEOUT_MS                       = "60000"
    BUYER_CODE_FETCH_CONFIG                       = <<EOF
    {
        "protectedAppSignalsBiddingJsUrl": "<URL to Protected App Signals generateBid UDF>",
        "urlFetchTimeoutMs": 60001, # This has to be > 1 minute.
        "urlFetchPeriodMs": 13000000,
        "prepareDataForAdsRetrievalJsUrl": "<URL to the UDF>"
    }
    EOF

  }  # runtime_flags

}  # Module "buyer"

הגדרה של טכנולוגיות פרסום למודעות של בית העסק

נשתמש בקובץ ההדגמה הזה בתור דוגמה, והמוכרים צריכים להגדיר את הסימונים הבאים. (הערה: רק ההגדרה של Protected App Signals מודגשת כאן). טכנולוגיות הפרסום צריכות לוודא שהן מחליפות את כתובות ה-URL הנכונות ב-placeholders:

module "seller" {
  # ... More config here.

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"

    SELLER_CODE_FETCH_CONFIG                           = <<EOF
  {
    "urlFetchTimeoutMs": 60001, # This has to be > 1 minute.
    "urlFetchPeriodMs": 13000000,
    "protectedAppSignalsBuyerReportWinJsUrls": {"<Buyer Domain>": "URL to reportWin UDF"}
  }
  EOF

  }  # runtime_flags

}  # Module "seller"

שירותי השוואת מחירים ואחזור מודעות

בהתאם לאסטרטגיות שנבחרו שתומכות באחזור המודעות, המערכת תדרוש פריסה של מקרה אחד או שניים של שירות המפתחות (KV). אנחנו נתייחס למכונה של KV שמשמשת לאחזור מודעות על סמך TEE בתור Ad Retrieval Server, ולמכונה שתתמוך באחזור שמבוסס על נתיב לפי הקשר בתור KV Lookup Server.

בשני המקרים, פריסת השרתים תואמת למסמכי התיעוד הזמינים בשרת KV GitHub. ההבדל בין שני המקרים הוא שבקשת החיפוש מתבצעת ללא צורך בהגדרה נוספת. לעומת זאת, במהלך האחזור צריך לפרוס את ה-UDF HandleRequest כדי להטמיע את לוגיקת האחזור. פרטים נוספים זמינים במדריך לתחילת השימוש בשרת KV. חשוב לזכור ש-B&A מצפה ששני השירותים ייפרסו באותה רשת Service mesh כמו זו של שירות הבידינג.

הגדרה לדוגמה

שימו לב לתרחיש הבא: באמצעות Protected App Signals API, טכנולוגיית פרסום שומרת אותות רלוונטיים על סמך השימוש באפליקציה של המשתמשים. בדוגמה שלנו מאוחסנים אותות שמייצגים רכישות מתוך מספר אפליקציות. במהלך מכרז, האותות המוצפנים נאספים ומועברים למכרז מוגן שפועל ב-B&A. פונקציות ה-UDF של הקונה שפועלות בB&A משתמשים באותות כדי לאחזר מועמדים להצגת מודעות ולחשב הצעת מחיר.

[Buyer] דוגמאות לאותות

הוספת אות עם מפתח 0 וערך של 1.

{
  "put": {
    "AA==": "AQ=="
  },
  "update_encoder": {
    "action": "REGISTER",
    "endpoint": "https://example.com/example_script"
  }
}

הוספת אות עם מפתח 1 וערך של 2.

{
  "put": {
    "AQ==": "Ag=="
  },
  "update_encoder": {
    "action": "REGISTER",
    "endpoint": "https://example.com/example_script"
  }
}

[Buyer] דוגמה ל-encodeSignals

כל אות מקודד לשני בייטים. הבייט הראשון הוא הבייט הראשון של מפתח האות והבייט השני הוא הבייט הראשון של ערך האות.

function encodeSignals(signals, maxSize) {
  // if there are no signals don't write a payload
  if (signals.size === 0) {
      return {};
  }

  let result = new Uint8Array(signals.size * 2);
  let index = 0;
  
  for (const [key, values] of signals.entries()) {
    result[index++] = key[0];
    result[index++] = values[0].signal_value[0];
  }
  
  return { 'status': 0, 'results': result};
}

[Buyer] דוגמה ל-PrepareDataForAdRetrieval

/**
 * `encodedOnDeviceSignals` is a Uint8Array and would contain
 * the app signals emanating from device. For purpose of the
 * demo, in our sample example, we assume that device is sending
 * the signals with pair of bytes formatted as following:
 * "<id><In app spending>". Where id corresponds to an ad category
 * that user uses on device, and the in app spending is a measure
 * of how much money the user has spent in this app category
 * previously. In our example, id of 0 will correspond to a
 * fitness ad category and a non-zero id will correspond to
 * food app category -- though this info will be useful
 * later in the B&A pipeline.
 *
 * Returns a JSON object indicating what type of ad(s) may be
 * most relevant to the user. In a real setup ad techs might
 * want to decode the signals as part of this script.
 *
 * Note: This example script makes use of only encoded device signals
 * but adtech can take other signals into account as well to prepare
 * the data that will be useful down stream for ad retrieval and
 * bid generation. The max length of the app signals used in this
 * sample example is arbitrarily limited to 4 bytes.
 */
function prepareDataForAdRetrieval(encodedOnDeviceSignals,
                                   encodedOnDeviceSignalsVersion,
                                   sellerAuctionSignals,
                                   contextualSignals) {
  if (encodedOnDeviceSignals.length === 0 || encodedOnDeviceSignals.length > 4 ||
      encodedOnDeviceSignals.length % 2 !== 0) {
     throw "Expected encoded signals length to be an even number in (0, 4]";
  }

  var preparedDataForAdRetrieval = {};
  for (var i = 0; i < encodedOnDeviceSignals.length; i += 2) {
    preparedDataForAdRetrieval[encodedOnDeviceSignals[i]] = encodedOnDeviceSignals[i + 1];
  }
  return preparedDataForAdRetrieval;
}

[קונים] UDF לאחזור מודעות לדוגמה

בדוגמה שלנו, השרת לאחזור מודעות שולח מטא-נתונים (כלומר מזהה לכל מודעה בדוגמה זו, אבל הוא יכול להכיל נתונים אחרים על כל מודעה שיכולים לעזור ביצירת הצעות מחיר מאוחר יותר) לכל אחד מ-k המודעות הפוטנציאליות המובילות.

function HandleRequest(requestMetadata, protectedSignals, deviceMetadata,
                      contextualSignals) {
 return "[{\"adId\":\"0\"},{\"adId\":\"1\"}]"

דוגמה ל-generateBid

/**
 * This script receives the data returned by the ad retrieval service
 * in the `ads` argument. This argument is supposed to contain all
 * the Protected App Signals related ads and the metadata obtained from the retrieval
 * service.
 *
 * `preparedDataForAdRetrieval` argument contains the data returned
 * from the `prepareDataForAdRetrieval` UDF.
 *
 * This script is responsible for generating bids for the ads
 * collected from the retrieval service and ad techs can decide to
 * run a small inference model as part of this script in order to
 * decide the best bid given all the signals available to them.
 *
 * For the purpose of the demo, this sample script assumes
 * that ad retrieval service has sent us most relevant ads for the
 * user and this scripts decides on the ad render URL as well as
 * what value to bid for each ad based on the previously decoded
 * device signals. For simplicity sake, this script only considers
 * 2 types of app categories i.e. fitness and food.
 *
 * Note: Only one bid is returned among all the
 * input ad candidates.
 */
function generateBid(ads, sellerAuctionSignals, buyerSignals, preparedDataForAdRetrieval) {
  if (ads === null) {
    console.log("No ads obtained from the ad retrieval service")
    return {};
  }     
        
  const kFitnessAd = "0";
  const kFoodAd = "1";
  const kBuyerDomain = "https://buyer-domain.com";
        
  let resultingBid = 0;
  let resultingRender = kBuyerDomain + "/no-ad";
  for (let i = 0 ; i < ads.length; ++i) {
    let render = "";
    let bid = 0;
    switch (ads[i].adId) {
      case kFitnessAd:
        render = kBuyerDomain + "/get-fitness-app";
        bid = preparedDataForAdRetrieval[kFitnessAd];
        break;
      case kFoodAd:
        render = kBuyerDomain + "/get-fastfood-app";
        bid = preparedDataForAdRetrieval[kFoodAd];
        break;
      default:
        console.log("Unknown ad category");
        render = kBuyerDomain + "/no-ad";
        break;
    }
    console.log("Existing bid: " + resultingBid + ", incoming candidate bid: " + bid);
    if (bid > resultingBid) {
      resultingBid = bid;
      resultingRender = render;
    }
  }
  return {"render": resultingRender, "bid": resultingBid};
}

[קונים] דוגמה לדוח מנצח

רכיב ה-UDF reportWin מדווח לקונה שהוא זכה במכרז.

function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                                       buyerReportingSignals, directFromSellerSignals,
                                       egressPayload,
                                       temporaryUnlimitedEgressPayload) {
  sendReportTo("https://buyer-controlled-domain.com/");
  registerAdBeacon({"clickEvent":"https://buyer-controlled-domain.com/clickEvent"});
  return;
}

[בית עסק] הגדרת שרת KV

המפיצים חייבים להגדיר שרת KV של אותות ציון, כדי שיהיה מיפוי זמין מכתובות ה-URL של עיבוד המודעות לאותות ציון תואמים. לדוגמה: אם הקונה מחזיר את הערכים https:/buyer-domain.com/get-fitness-app ו-https:/buyer-domain.com/get-fastfood-app, המוכר יכול לקבל את התגובה הבאה לאותות ניקוד לדוגמה כאשר הוא שולח שאילתה ל-SFE באמצעות GET ב-https://key-value-server-endpoint.com?client_type=1&renderUrls=<render-url-returned-by-the-buyer>:

{
   "renderUrls" : {
      "https:/buyer-domain.com/get-fitness-app" : [
         "1",
         "2"
      ],
      "https:/buyer-domain.com/get-fastfood-app" : [
         "3",
         "4"
      ]
   }
}

דוגמה למודעת ציון של [מפיץ]

/**
 * This module generates a random desirability score for the Protected App
 * Signals ad in this example. In a production deployment,
 * however, the sellers would want to use all the available signals to generate
 * a score for the ad.
 */
function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

function scoreAd(adMetadata, bid, auctionConfig,
                                   trustedScoringSignals, deviceSignals,
                                   directFromSellerSignals) {
  return {
    "desirability": getRandomInt(10000),
    "allowComponentAuction": false
  };
}

דוגמה לתוצאה של דוח [מפיץ]

function reportResult(auctionConfig, sellerReportingSignals, directFromSellerSignals){
  let signalsForWinner = {};
    sendReportTo("https://seller-controlled-domain.com");
    registerAdBeacon({"clickEvent":
                    "https://seller-controlled-domain.com/clickEvent"});
    return signalsForWinner;
}

אפליקציה לדוגמה

כדוגמה לאופן שבו אפשר להשתמש ב-API כדי ליצור אפליקציה שמשתמשת בזרימה פשוטה כמו שמתואר למעלה, יצרנו אפליקציה לדוגמה של Protected App Signals, שבמאגר לדוגמה הזה אפשר למצוא.