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

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

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

ינואר 2024

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

מרץ 2024

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

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

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

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

  • ממשקי API ל-Android:
    • Signal Curation API, שמורכב מ:
    • Update Signals API
    • Signals Encoding API
    • Protected Auction Support API: ממשק API לתמיכה במכרזים מוגנים, שמשמש את ערכות ה-SDK להפעלת המכרז המוגן בשרתים של הבידינג והמכרזים (B&A) באמצעות אותות מוגנים של אפליקציות.
  • ממשקי API בצד השרת:
    • Protected Auction API: סדרה של סקריפטים של JS שפועלים בשרתים של הבידינג והמכרזים. ה-API הזה מאפשר למוכרים ולקונים לכתוב את הלוגיקה להטמעת המכרז המוגן.
    • Ad Retrieval API: אחראי לספק רשימה של מודעות מועמדות על סמך המידע על ההקשר והמשתמשים שזמין לשרת הבידינג של הקונה.

לקוח Android

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

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

Update Signals API

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

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

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

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 חייבים להתאים לאחת מחמש הפקודות הבאות:

key

תיאור

put

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

for הוא אובייקט JSON שבו המפתחות הם מחרוזות base64 שתואמות למפתח שרוצים להוסיף, והערכים הם מחרוזות base64 שתואמות לערך שרוצים להוסיף.

append

הוספת אותות חדשים לסדרת אותות לאורך זמן, והסרה של האות הישן ביותר

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

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

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

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

put_if_not_present

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

remove

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

update_encoder

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

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

הערכים הנתמכים כרגע הם רק 'REGISTER', שיירשם את נקודת הקצה של הקודק אם הוא מסופק בפעם הראשונה, או יחליף את הנקודה הקיימת בנקודת הקצה החדשה שצוינה. חובה לספק את נקודת הקצה (endpoint) בפעולה 'REGISTER'. מפתח המשנה לספק נקודת קצה של מקודד הוא '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"
    }
}

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

Signals Encoding API

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

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 לרשימות של אובייקטים של אותות מוגנים של אפליקציות. לכל אובייקט של Protected App Signals יש שלושה שדות:

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

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

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

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

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

קידוד אותות

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

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

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

Response header for providing encoder version : X_ENCODER_VERSION

Protected Auction Support API

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

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

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

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

Protected Auction API

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

משתני UDF של טכנולוגיות פרסום של הקונה
פונקציית UDF prepareDataForAdRetrieval

כדי שאפשר יהיה להשתמש באותות של אפליקציות מוגנות כדי לאחזר מועמדות למודעות משירות אחזור המודעות של 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 של generateBid

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

פונקציית UDF של reportWin

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

// Inputs / Outputs
// ----------------
// See detailed documentation here.
function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                   buyerReportingSignals,
                   egressPayload, temporaryUnlimitedEgressPayload) {
  // ...
}
משתני UDF של טכנולוגיות פרסום של מוכרים
פונקציית UDF של scoreAd

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

function scoreAd(adMetadata, bid, auctionConfig,
                 trustedScoringSignals, bid_metadata) {
  // ...
  return {desirability: desirabilityScoreForThisAd,
              allowComponentAuction: true_or_false};
}
פונקציית UDF של reportResult

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

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

Ad Retrieval API

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

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

פונקציית UDF ‏getCandidateAds
function getCandidateAds(requestMetadata, preparedDataForAdRetrieval,
                      deviceMetadata, contextualSignals, contextualAdIds,) {
    return adsMetadataString;
}

כאשר:

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

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

הגדרת המערכת לפיתוח

Android

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

  1. יוצרים מכשיר פיזי או אמולטור (מועדף) שבו פועלת התמונה של Developer Preview 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 (ראו הקטע הרלוונטי).
  • מוודאים שכל פונקציות ה-UDF הנדרשות (prepareDataForAdRetrieval, ‏ generateBid, ‏ reportWin, ‏ getCandidateAds) מוגדרות ומתארחות בפלטפורמת טכנולוגיית הפרסום.

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

הגדרות של Terraform

כדי להשתמש ב-Protected App Signals, טכנאי הפרסום צריכים:

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

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

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

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

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

טכנאי הפרסום צריכים להחליף את כתובות ה-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"

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

בדוגמה של הקובץ demo, המוכרים צריכים להגדיר את הדגלים הבאים. (הערה: כאן מודגשות רק ההגדרות שקשורות ל-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. נתייחס למכונה של מכונה ה-KV שמשמשת לאחזור מודעות מבוסס-TEE בתור Ad Retrieval Server, ולמכונה שתומכת באחזור מבוסס-נתיב לפי הקשר בתור KV Lookup Server.

בשני המקרים, פריסת השרתים מתבצעת בהתאם למסמכים שזמינים ב-GitHub של שרת ה-KV. ההבדל בין שני המקרים הוא שבמקרה של חיפוש, הפתרון פועל מתוך האריזה בלי הגדרות נוספות, ואילו במקרה של אחזור, צריך לפרוס את פונקציית ה-UDF ‏getCandidateAds כדי להטמיע את הלוגיקה של האחזור. פרטים נוספים זמינים במדריך לתחילת העבודה עם שרת 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 getCandidateAds(requestMetadata, protectedSignals, deviceMetadata,
                      contextualSignals,   contextualAdIds,) {
 return "[{\"adId\":\"0\"},{\"adId\":\"1\"}]"

[Buyers] דוגמה ל-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};
}

[Buyers] דוגמה לדוח win

פונקציית ה-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;
}

[Seller] הגדרת שרת 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"
      ]
   }
}

[Seller] scoreAd example

/**
 * 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
  };
}

דוגמה ל-reportResult ב-[Seller]

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

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

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