Przewodnik dla programistów interfejsu Attribution Reporting API

Podczas czytania dokumentacji dotyczącej Piaskownicy prywatności na Androida użyj przycisku Podgląd dla deweloperów lub Beta, aby wybrać wersję programu, z którą pracujesz, ponieważ instrukcje mogą się różnić.


Interfejs Attribution Reporting API ma zapewniać lepszą ochronę prywatności użytkowników dzięki wyeliminowaniu zależności od takich identyfikatorów (własnych i pochodzących od firm zewnętrznych) oraz obsługiwać najważniejsze przypadki pomiaru konwersji i przypisywania udziału w konwersji w aplikacjach. Ten przewodnik dla deweloperów opisuje, jak skonfigurować i przetestować interfejsy Attribution Reporting API, aby rejestrować kliknięcia, wyświetlenia i konwersje reklam poprzez wywołanie metod rejestrujących odpowiednie reguły i źródła tych zdarzeń.

Z tego przewodnika dowiesz się, jak skonfigurować punkty końcowe serwera i utworzyć aplikację klienta, która wywołuje te usługi. Więcej informacji o ogólnej strukturze interfejsu Attribution Reporting API znajdziesz w propozycji projektowej.

Hasła kluczowe

  • Źródła atrybucji odnoszą się do kliknięć lub wyświetleń.
  • Wyzwalacze to zdarzenia, które można przypisać do konwersji.
  • Raporty zawierają dane o wyzwalaczu i odpowiednim źródle atrybucji. Te raporty są wysyłane w odpowiedzi na zdarzenia uruchamiające. Interfejs Attribution Reporting API obsługuje raporty na poziomie zdarzeniaraporty zbiorcze.

Zanim zaczniesz

Aby korzystać z interfejsu Attribution Reporting API, wykonaj zadania po stronie serwera i klienta wymienione w następnych sekcjach.

Konfigurowanie punktów końcowych interfejsu Attribution Reporting API

Interfejs Attribution Reporting API wymaga zestawu punktów końcowych, do których możesz uzyskać dostęp z urządzenia testowego lub emulatora. Utwórz po 1 punkcie końcowym dla każdej z tych czynności po stronie serwera:

Wymagane punkty końcowe można skonfigurować na kilka sposobów:

  • Najszybszym sposobem na rozpoczęcie pracy jest wdrożenie definicji usług OpenAPI v3 z naszego repozytorium przykładowego kodu na platformie mock lub mikrousług. Możesz użyć Postmana, Prisma lub innej platformy z symulowanym serwerem, która obsługuje ten format. Wdrożyć każdy punkt końcowy i śledzić URI do użycia w aplikacji. Aby sprawdzić dostarczenie raportu, sprawdź wywołania wykonane wcześniej do platformy mock lub bez serwera.
  • Uruchom własny serwer samodzielny, korzystając z przykładowego kodu Kotlina opartego na Spring Boot. Wdróż ten serwer u dostawcy usług w chmurze lub w wewnętrznej infrastrukturze.
  • Definicje usług możesz wykorzystać jako przykłady integracji punktów końcowych z istniejącym systemem.

Akceptowanie rejestracji źródła

Ten punkt końcowy powinien być adresowalny za pomocą identyfikatora URI podobnego do tego:

https://adtech.example/attribution_source

Gdy aplikacja kliencka zarejestruje źródło atrybucji, podaje adres URI tego punktu końcowego serwera. Interfejs Attribution Reporting API wysyła żądanie i zawiera jeden z tych nagłówków:

  • W przypadku zdarzeń kliknięcia:

    Attribution-Reporting-Source-Info: navigation
    
  • W przypadku wyświetlania wydarzeń:

    Attribution-Reporting-Source-Info: event
    

Skonfiguruj punkt końcowy serwera, aby odpowiadał w ten sposób:

// Metadata associated with attribution source.
Attribution-Reporting-Register-Source: {
  "destination": "[app package name]",
  "web_destination": "[eTLD+1]",
  "source_event_id": "[64 bit unsigned integer]",
  "expiry": "[64 bit signed integer]",
  "event_report_window": "[64-bit signed integer]",
  "aggregatable_report_window": "[64-bit signed integer]",
  "priority": "[64 bit signed integer]",
  "filter_data": {
    "[key name 1]": ["key1 value 1", "key1 value 2"],
    "[key name 2]": ["key2 value 1", "key2 value 2"],
    // Note: "source_type" key will be automatically generated as
    // one of {"navigation", "event"}.
  },
  // Attribution source metadata specifying histogram contributions in aggregate
  // report.
  "aggregation_keys": {
    "[key1 name]": "[key1 value]",
    "[key2 name]": "[key2 value]",
  },

    "debug_key": "[64-bit unsigned integer]",
    "debug_reporting": [boolean]
}
// Specify additional ad tech URLs to register this source with.
Attribution-Reporting-Redirect: <Ad Tech Partner URI 1>
Attribution-Reporting-Redirect: <Ad Tech Partner URI 2>

Oto przykład z dodanymi przykładowymi wartościami:

Attribution-Reporting-Register-Source: {
  "destination": "android-app://com.example.advertiser",
  "source_event_id": "234",
  "expiry": "259200",
  "event_report_window": "172800",
  "aggregatable_report_window": "172800",
  "priority": "5",
  "filter_data": {
    "product_id": ["1234"]
  },
  "aggregation_keys": {
  // Generates a "0x159" key piece named (low order bits of the key) for the key
  // named "campaignCounts".
  // User saw an ad from campaign 345 (out of 511).
    "campaignCounts": "0x159",

  // Generates a "0x5" key piece (low order bits of the key) for the key named
  // "geoValue".
  // Source-side geo region = 5 (US), out of a possible ~100 regions.
    "geoValue": "0x5",
  },
  // Opts in to receiving verbose debug reports
  "debug_reporting": true
}

Attribution-Reporting-Redirect:
https://adtechpartner1.example?their_ad_click_id=567
Attribution-Reporting-Redirect:
https://adtechpartner2.example?their_ad_click_id=890

Jeśli Attribution-Reporting-Redirects zawiera URI partnerów technologii reklamowych, interfejs Attribution Reporting API wysyła podobne żądanie do każdego z nich. Każdy partner technologiczny reklam musi skonfigurować serwer, który odpowiada tymi nagłówkami:

Attribution-Reporting-Register-Source: {
  "destination": "[app package name]",
  "web_destination": "[eTLD+1]",
  "source_event_id": "[64 bit unsigned integer]",
  "expiry": "[64 bit signed integer]",
  "event_report_window": "[64-bit signed integer]",
  "aggregatable_report_window": "[64-bit signed integer]",
  "priority": "[64 bit signed integer]",
  "filter_data": {
    "[key name 1]": ["key1 value 1", "key1 value 2"],
    "[key name 2]": ["key2 value 1", "key2 value 2"],
    // Note: "source_type" key will be automatically generated as
    // one of {"navigation", "event"}.
  },
  "aggregation_keys": {
    "[key1 name]": "[key1 value]",
    "[key2 name]": "[key2 value]",
  }
}
// The Attribution-Reporting-Redirect header is ignored for ad tech partners.

Akceptowanie rejestracji wyzwalacza konwersji

Ten punkt końcowy powinien być adresowalny za pomocą identyfikatora URI podobnego do tego:

https://adtech.example/attribution_trigger

Gdy aplikacja kliencka zarejestruje zdarzenie wyzwalające, podaje adres URI tego punktu końcowego serwera. Interfejs Attribution Reporting API wysyła wtedy żądanie, dołączając jeden z tych nagłówków:

Skonfiguruj punkt końcowy serwera, aby odpowiadał w ten sposób:

// Metadata associated with trigger.
Attribution-Reporting-Register-Trigger: {
  "event_trigger_data": [{
    // "trigger_data returned" in event reports is truncated to
    // the last 1 or 3 bits, based on conversion type.
    "trigger_data": "[unsigned 64-bit integer]",
    "priority": "[signed 64-bit integer]",
    "deduplication_key": "[signed 64-bit integer]",
    // "filter" and "not_filters" are optional fields which allow configuring
    // event trigger data based on source's filter_data. They consist of a
    // filter set, which is a list of filter maps. An event_trigger_data object
    // is ignored if none of the filter maps in the set match the source's
    // filter data.
    // Note: "source_type" can be used as a key in a filter map to filter based
    // on the source's "navigation" or "event" type. The first
    // Event-Trigger that matches (based on the filters/not_filters) will be
    // used for report generation. If none of the event-triggers match, no
    // event report will be generated.
    "filters": [{
      "[key name 1]": ["key1 value 1", "key1 value 2"],
      // If a key is missing from filters or source's filter_data, it won't be
      // used during matching.
      "[key name 2]": ["key2 value 1", "key2 value 2"],
    }],
    "not_filters":  [{
      "[key name 1]": ["key1 value 1", "key1 value 2"],
      // If a key is missing from not_filters or source's filter_data, it won't
      // be used during matching.
      "[key name 2]": ["key2 value 1", "key2 value 2"],
    }]
  }],
  // Specify a list of dictionaries that generates aggregation keys.
  "aggregatable_trigger_data": [
    // Each dictionary entry independently adds pieces to multiple source keys.
    {
      "key_piece": "[key piece value]",
      "source_keys": ["[key name the key piece value applies to]",
      ["list of IDs in source to match. Non-matching IDs are ignored"]]
      // filters/not_filters are optional fields similar to event trigger data
      // filter fields.
      "filters": [{
        "[key name 1]": ["key1 value 1", "key1 value 2"]
      }],
      "not_filters":  [{
          "[key name 1]": ["key1 value 1", "key1 value 2"],
          "[key name 2]": ["key2 value 1", "key2 value 2"],
      }]
    },
    ..
  ],
  // Specify an amount of an abstract value which can be integers in [1, 2^16]
  // to contribute to each key that is attached to aggregation keys in the
  // order they are generated.
  "aggregatable_values": [
     // Each source event can contribute a maximum of L1 = 2^16 to the
     // aggregate histogram.
    {
     "[key_name]": [value]
    },
    ..
  ],
  aggregatable_deduplication_keys: [{
  deduplication_key": [unsigned 64-bit integer],
    "filters": {
        "category": [filter_1, …, filter_H]
      },
    "not_filters": {
        "category": [filter_1, …, filter_J]
      }
  },
  ...
  {
  "deduplication_key": [unsigned 64-bit integer],
    "filters": {
        "category": [filter_1, …, filter_D]
      },
    "not_filters": {
        "category": [filter_1, …, filter_J]
      }
    }
  ]

  "debug_key": "[64-bit unsigned integer]",
  "debug_reporting": [boolean]

}
// Specify additional ad tech URLs to register this trigger with.
// Repeated Header field "Attribution-Reporting-Redirect"
Attribution-Reporting-Redirect: <Ad Tech Partner URI 1>
Attribution-Reporting-Redirect: <Ad Tech Partner URI 2>

Oto przykład z dodanymi przykładowymi wartościami:

Attribution-Reporting-Register-Trigger: {
  "event_trigger_data": [{
    "trigger_data": "1122", // Returns 010 for CTCs and 0 for VTCs in reports.
    "priority": "3",
    "deduplication_key": "3344"
    "filters": [{ // Filter strings can not exceed 25 characters
      "product_id": ["1234"],
      "source_type": ["event"]
    }]
  },
  {
    "trigger_data": "4", // Returns 100 for CTCs and 0 for VTCs in reports.
    "priority": "3",
    "deduplication_key": "3344"
    "filters": [{ // Filter strings can not exceed 25 characters
      "product_id": ["1234"],
      "source_type": ["navigation"]
    }]
  }],
  "aggregatable_trigger_data": [
    // Each dictionary independently adds pieces to multiple source keys.
    {
      // Conversion type purchase = 2 at a 9-bit offset, i.e. 2 << 9.
      // A 9-bit offset is needed because there are 511 possible campaigns,
      // which takes up 9 bits in the resulting key.
      "key_piece": "0x400",// Conversion type purchase = 2
      // Apply this key piece to:
      "source_keys": ["campaignCounts"]
       // Filter strings can not exceed 25 characters
    },
    {
      // Purchase category shirts = 21 at a 7-bit offset, i.e. 21 << 7.
      // A 7-bit offset is needed because there are ~100 regions for the geo
      // key, which takes up 7 bits of space in the resulting key.
      "key_piece": "0xA80",
      // Apply this key piece to:
      "source_keys": ["geoValue", "nonMatchingIdsAreIgnored"]
      // source_key values must not exceed the limit of 25 characters
    }
  ],
  "aggregatable_values":
    {
      // Privacy budget for each key is L1 / 2 = 2^15 (32768).
      // Conversion count was 1.
      // Scale the count to use the full budget allocated: 1 * 32768 = 32768.
      "campaignCounts": 32768,

      // Purchase price was $52.
      // Purchase values for the app range from $1 to $1,024 (integers only).
      // Scaling factor applied is 32768 / 1024 = 32.
      // For $52 purchase, scale the value by 32 ($52 * 32 = $1,664).
      "geoValue": 1664
    }
  ,
  // aggregatable_deduplication_keys is an optional field. Up to 50 "keys"
  // can be included in the aggregatable_deduplication_keys list. Filters, not
  // filters, and deduplication_key are optional fields. If deduplication_key
  // is omitted, it will be treated as a null value. See
  // https://wicg.github.io/attribution-reporting-api/#triggering-aggregatable-attribution
  aggregatable_deduplication_keys:
  [
    {
    deduplication_key": 3,
        "filters": {
          "category": [A]
        }
    },
    {
    "deduplication_key": 4,
        "filters": {
          "category": [C, D]
        },
        "not_filters": {
          "category": [F]
        }
    }
  ]
  // Opts into receiving verbose debug reports
  "debug_reporting": true
}
Attribution-Reporting-Redirect:https://adtechpartner.example?app_install=567

Limit wynosi 25 bajtów na identyfikator klucza agregacji i ciąg znaków filtra. Oznacza to, że identyfikatory kluczy agregacji i ciągi znaków filtra nie mogą mieć więcej niż 25 znaków. W tym przykładzie ciąg campaignCounts ma 14 znaków, więc jest prawidłowym identyfikatorem klucza agregacji, a ciąg 1234 ma 4 znaki, więc jest prawidłowym ciągiem filtra. Jeśli ciąg znaków identyfikatora klucza agregacji lub ciągu filtra przekracza 25 znaków, reguła jest ignorowana.

Jeśli Attribution-Reporting-Redirect zawiera URI partnerów technologii reklamowych, interfejs Attribution Reporting API wysyła podobne żądanie do każdego z nich. Każdy partner technologiczny reklam musi skonfigurować serwer, który odpowiada tymi nagłówkami:

// Metadata associated with trigger.
Attribution-Reporting-Register-Trigger: {
  "event_trigger_data": [{
    // "trigger_data" returned in event reports is truncated to
    // the last 1 or 3 bits, based on conversion type.
    "trigger_data": "[unsigned 64-bit integer]",
    "priority": "[signed 64-bit integer]",
    "deduplication_key": "[signed 64-bit integer]",
    // filter and not_filters are optional fields which allow configuring
    // different event trigger data based on source's filter_data. They
    // consist of a filter set, which is a list of filter maps. An
    // event_trigger_data object is ignored if none of the filter maps in the
    // set match the source's filter data. Note: "source_type" can be used as
    // a key in a filter map to filter based on the source's "navigation" or
    // "event" type. The first Event-Trigger that matches (based on the
    // filters/not_filters) will be used for report generation. If none of the
    // event-triggers match, no report will be generated.
    "filters": [{
      "[key name 1]": ["key1 value 1", "key1 value 2"],
      // If a key is missing from filters or source's filter_data, it will not be
      // used during matching.
      "[key name 2]": ["key2 value 1", "key2 value 2"],
    }],
    "not_filters":  [{
      "[key name 1]": ["key1 value 1", "key1 value 2"],
      // If a key is missing from not_filters or source's filter_data, it will not
      // be used during matching.
      "[key name 2]": ["key2 value 1", "key2 value 2"],
    }]
  }],
  "aggregatable_trigger_data": [
    // Each dictionary entry independently adds pieces to multiple source keys.
    {
      "key_piece": "[key piece value]",
      "source_keys": ["[key name the key piece value applies to]",
      ["list of IDs in source to match. Non-matching IDs are ignored"]],
      // filters/not_filters are optional fields similar to event trigger data
      // filter fields.
      "filters": [{
        "[key name 1]": ["key1 value 1", "key1 value 2"]
      }],
      "not_filters":  [{
          "[key name 1]": ["key1 value 1", "key1 value 2"],
          "[key name 2]": ["key2 value 1", "key2 value 2"],
      }]
    },
    ..
  ],
  // Specify an amount of an abstract value which can be integers in [1, 2^16] to
  // contribute to each key that is attached to aggregation keys in the order they
  // are generated.
  "aggregatable_values": [
    // Each source event can contribute a maximum of L1 = 2^16 to the aggregate
    // histogram.
    {
     "[key_name]": [value]
    }
  ]
}
// The Attribution-Reporting-Redirect header is ignored for ad tech partners.

Akceptowanie raportów na poziomie zdarzenia

Ten punkt końcowy powinien być adresowalny za pomocą identyfikatora URI. Aby dowiedzieć się więcej o rejestrowaniu adresów URI, przeczytaj artykuł Rejestrowanie konta w Piaskownicy prywatności. (Identyfikator URI jest wywnioskowany na podstawie pochodzenia serwerów używanych do akceptowania rejestracji źródła i wyzwalania rejestracji). Przykładowe identyfikatory URI punktów końcowych, które akceptują rejestrację źródeł i akceptują rejestrację wyzwalaczy:

https://adtech.example/.well-known/attribution-reporting/report-event-attribution

Skonfiguruj ten serwer tak, aby akceptował żądania JSON o tym formacie:

{
  "attribution_destination": "android-app://com.advertiser.example",
  "source_event_id": "12345678",
  "trigger_data": "2",
  "report_id": "12324323",
  "source_type": "navigation",
  "randomized_trigger_rate": "0.02"
   [Optional] "source_debug_key": "[64-bit unsigned integer]",
   [Optional] "trigger_debug_key": "[64-bit unsigned integer]",
}

Klucze debugowania umożliwiają uzyskanie dodatkowych statystyk w raportach atrybucji. Dowiedz się więcej o ich konfigurowaniu.

Akceptowanie raportów zbiorczych

Ten punkt końcowy powinien być adresowalny za pomocą identyfikatora URI. Aby dowiedzieć się więcej o rejestrowaniu adresów URI, przeczytaj artykuł Rejestrowanie konta w Piaskownicy prywatności. (Identyfikator URI jest wywnioskowany na podstawie pochodzenia serwerów używanych do akceptowania rejestracji źródła i wyzwalania rejestracji). Przykładowe identyfikatory URI punktów końcowych, które akceptują rejestrację źródeł i akceptują rejestrację wyzwalaczy:

https://adtech.example/.well-known/attribution-reporting/report-aggregate-attribution

W przypadku raportów umożliwiających agregację wypełniane są zarówno pola zaszyfrowane, jak i niezaszyfrowane. Zaszyfrowane raporty umożliwiają rozpoczęcie testowania za pomocą usługi agregacji, a niezaszyfrowane pole zapewnia informacje o sposobie, w jaki zestawy par klucz-wartość strukturyzują dane.

Skonfiguruj ten serwer tak, aby akceptował żądania JSON o tym formacie:

{
  // Info that the aggregation services also need encoded in JSON
  // for use with AEAD. Line breaks added for readability.
  "shared_info": "{
     \"api\":\"attribution-reporting\",
     \"attribution_destination\": \"android-app://com.advertiser.example.advertiser\",
     \"scheduled_report_time\":\"[timestamp in seconds]\",
     \"source_registration_time\": \"[timestamp in seconds]\",
     \"version\":\"[api version]\",
     \"report_id\":\"[UUID]\",
     \"reporting_origin\":\"https://reporter.example\" }",

  // In the current Developer Preview release, The "payload" and "key_id" fields
  // are not used because the platform does not yet encrypt aggregate reports.
  // Currently, the "debug_cleartext_payload" field holds unencrypted reports.
  "aggregation_service_payloads": [
    {
      "payload": "[base64 HPKE encrypted data readable only by the aggregation service]",
      "key_id": "[string identifying public key used to encrypt payload]",

      "debug_cleartext_payload": "[unencrypted payload]"
    },
  ],

  "source_debug_key": "[64 bit unsigned integer]",
  "trigger_debug_key": "[64 bit unsigned integer]"
}

Klucze debugowania umożliwiają uzyskanie dodatkowych statystyk w raportach atrybucji. Dowiedz się więcej o ich konfigurowaniu.

Konfigurowanie klienta na Androida

Aplikacja klienta rejestruje źródła i wyzwalacze atrybucji oraz umożliwia generowanie raportów na poziomie zdarzenia i zbiorczych. Aby przygotować urządzenie klienta Android lub jego emulator do korzystania z interfejsu Attribution Reporting API, wykonaj te czynności:

  1. Skonfiguruj środowisko programistyczne do korzystania z Piaskownicy prywatności na Androida.
  2. Zainstaluj obraz systemu na obsługiwanym urządzeniu lub skonfiguruj emulator, który obsługuje Piaskownicę prywatności na Androidzie.
  3. Włącz dostęp do interfejsu Attribution Reporting API, uruchamiając to polecenie ADB. (domyślnie jest on wyłączony).

    adb shell device_config put adservices ppapi_app_allow_list \"\*\"
  4. Jeśli testujesz lokalnie interfejs Attribution Reporting API (np. na urządzeniu, do którego masz fizyczny dostęp), uruchom to polecenie, aby wyłączyć rejestrację:

    adb shell device_config put adservices disable_measurement_enrollment_check "true"
  5. W pliku manifestu Androida dodaj uprawnienie ACCESS_ADSERVICES_ATTRIBUTIONutwórz konfigurację usług reklamowych, aby aplikacja mogła korzystać z interfejsów Attribution Reporting API:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
    
  6. (Opcjonalnie) Jeśli planujesz otrzymywać raporty debugowania, dodaj uprawnienia ACCESS_ADSERVICES_AD_ID do pliku manifestu Androida:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
    
  7. Odwołuj się do konfiguracji usług reklamowych w elemencie <application> w pliku manifestu:

    <property android:name="android.adservices.AD_SERVICES_CONFIG"
              android:resource="@xml/ad_services_config" />
    
  8. Określ zasób XML usług reklamowych, do którego odwołuje się manifest, np. res/xml/ad_services_config.xml. Dowiedz się więcej o uprawnieniach usług reklamowych i kontroli dostępu pakietu SDK.

    <ad-services-config>
        <attribution allowAllToAccess="true" />
    </ad-services-config>
    

Rejestrowanie zdarzeń reklamy

Aby mieć pewność, że źródła i konwersje są prawidłowo raportowane, aplikacja powinna rejestrować je w miarę ich występowania. Klasa MeasurementManager zawiera metody, które ułatwiają rejestrowanie źródeł atrybucjiwyzwań konwersji.

Rejestrowanie zdarzenia źródła atrybucji

Gdy użytkownik wyświetla reklamę lub klika ją, aplikacja wydawcy wywołuje funkcję registerSource(), aby zarejestrować źródło atrybucji zgodnie z fragmentem kodu.

Interfejs Attribution Reporting API obsługuje te typy zdarzeń źródła atrybucji:

  • kliknięcia, które zwykle rejestrujesz w ramach metody wywołania zwrotnego podobnej do onClick(); Odpowiednie zdarzenie aktywujące występuje zwykle wkrótce po zdarzeniu kliknięcia. Zdarzenie tego typu dostarcza więcej informacji o interakcji użytkownika, dlatego jest dobrym źródłem atrybucji, któremu warto nadać wysoki priorytet.
  • Wyświetlenia, które zwykle rejestrujesz w ramach metody wywołania zwrotnego podobnej do onAdShown(). Odpowiednie zdarzenie wyzwalające może wystąpić po kilku godzinach lub dniach od zdarzenia wyświetlenia.

Kotlin

companion object {
    private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}

val measurementManager = context.getSystemService(MeasurementManager::class.java)
var exampleClickEvent: InputEvent? = null

// Use the URI of the server-side endpoint that accepts attribution source
// registration.
val attributionSourceUri: Uri =
  Uri.parse("https://adtech.example/attribution_source?AD_TECH_PROVIDED_METADATA")

val future = CompletableFuture<Void>()

adView.setOnTouchListener(_: View?, event: MotionEvent?)) ->
    exampleClickEvent = event
    true
}

// Register Click Event
measurementManager.registerSource(
        attributionSourceUri,
        exampleClickEvent,
        CALLBACK_EXECUTOR,
        future::complete)

// Register View Event
measurementManager.registerSource(
        attributionSourceUri,
        null,
        CALLBACK_EXECUTOR,
        future::complete)

Java

private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool();
private InputEvent exampleClickEvent;

MeasurementManager measurementManager =
        context.getSystemService(MeasurementManager.class);

// Use the URI of the server-side endpoint that accepts attribution source
// registration.
Uri attributionSourceUri =
Uri.parse("https://adtech.example/attribution_source?AD_TECH_PROVIDED_METADATA");

CompletableFuture<Void> future = new CompletableFuture<>();

adView.setOnTouchListener(v, event)) -> {
    exampleClickEvent = event;
    return true;
}

// Register Click Event
measurementManager.registerSource(attributionSourceUri, exampleClickEvent,
        CALLBACK_EXECUTOR, future::complete);

// Register View Event
measurementManager.registerSource(attributionSourceUri, null,
        CALLBACK_EXECUTOR, future::complete);

Po rejestracji interfejs API wysyła żądanie HTTP POST do punktu końcowego usługi pod adresem określonym przez attributionSourceUri. Odpowiedź punktu końcowego zawiera wartości destination, source_event_id, expirysource_priority.

Jeśli dostawca technologii reklamowej, który pierwotnie zarejestrował źródło, chce udostępnić rejestracje źródła, pierwotny identyfikator URI źródła atrybucji może zawierać przekierowania do innych punktów końcowych dostawcy technologii reklamowej. Limity i reguły dotyczące przekierowań są opisane w propozycji technicznej.

Dodaliśmy obsługę łańcuchowych przekierowań w przypadku registerSourceregisterTrigger. Oprócz nagłówka rejestracji użytkownik interfejsu API może teraz przesłać przekierowanie HTTP jako odpowiedź serwera, która zawiera kod stanu 302 i nagłówek „Location” z adresem URL kolejnej rejestracji.

W całym łańcuchu daisy-chain używane jest tylko pole „destination” (miejsce docelowe) podane w ramach pierwszej wizyty. Liczba wizyt ma ten sam limit co nagłówki „Attribution-Reporting-Redirect”. Obsługa przekierowań jest dodatkiem do dotychczasowej obsługi „Attribution-Reporting-Redirect”. Jeśli obie są obecne, pierwszeństwo ma „Attribution-Reporting-Redirect”.

Rejestrowanie zdarzenia wywołującego konwersję

Aby zarejestrować zdarzenie wyzwalczające konwersję, wywołaj funkcję registerTrigger() w aplikacji:

Kotlin

companion object {
    private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}

val measurementManager = context.getSystemService(MeasurementManager::class.java)

// Use the URI of the server-side endpoint that accepts trigger registration.
val attributionTriggerUri: Uri =
    Uri.parse("https://adtech.example/trigger?AD_TECH_PROVIDED_METADATA")

val future = CompletableFuture<Void>()

// Register trigger (conversion)
measurementManager.registerTrigger(
        attributionTriggerUri,
        CALLBACK_EXECUTOR,
        future::complete)

Java

private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool();

MeasurementManager measurementManager =
        context.getSystemService(MeasurementManager.class);

// Use the URI of the server-side endpoint that accepts trigger registration.
Uri attributionTriggerUri =
        Uri.parse("https://adtech.example/trigger?AD_TECH_PROVIDED_METADATA");

CompletableFuture<Void> future = new CompletableFuture<>();

// Register trigger (conversion)
measurementManager.registerTrigger(
        attributionTriggerUri,
        CALLBACK_EXECUTOR,
        future::complete)

Po rejestracji interfejs API wysyła żądanie HTTP POST do punktu końcowego usługi pod adresem określonym przez attributionTriggerUri. Odpowiedź punktu końcowego zawiera wartości dotyczące raportów o zdarzeniach i raportów zbiorczych.

Jeśli platforma adtech, z której pochodzą dane, umożliwia udostępnianie rejestracji zdarzeń, adres URI może zawierać przekierowania do adresów URI należących do innych platform adtech. Limity i zasady dotyczące przekierowań są opisane w propozycji technicznej.

Rejestrowanie pomiarów obejmujących aplikacje i witryny

W przypadku, gdy w ścieżce od źródła do wywołania uczestniczą zarówno aplikacja, jak i przeglądarka, występują subtelne różnice w implementacji rejestrowania zdarzeń reklamy. Jeśli użytkownik widzi reklamę w aplikacji i zostaje przekierowany do przeglądarki w celu dokonania konwersji, źródło jest rejestrowane przez aplikację, a konwersja jest rejestrowana przez przeglądarkę. Podobnie, jeśli użytkownik rozpoczyna korzystanie z usługi w przeglądarce, a następnie jest przekierowywany do aplikacji, w której dokonuje konwersji, przeglądarka rejestruje źródło, a aplikacja – konwersję.

Ze względu na różnice w organizacji technologii reklamowych w internecie i na Androidzie dodaliśmy nowe interfejsy API do rejestrowania źródeł i wyzwalaczy w przeglądarkach. Główna różnica między tymi interfejsami API a odpowiednimi interfejsami API opartymi na aplikacji polega na tym, że oczekujemy, że przeglądarka będzie podążać za przekierowaniami, stosować filtry specyficzne dla przeglądarki i przekazywać prawidłowe rejestracje na platformę, wywołując registerWebSource() lub registerWebTrigger().

Ten fragment kodu pokazuje przykład wywołania interfejsu API, które przeglądarka wykonuje, aby zarejestrować źródło atrybucji, zanim przekieruje użytkownika do aplikacji:

Kotlin

companion object {
    private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}

val measurementManager =
        context.getSystemService(MeasurementManager::class.java)
var exampleClickEvent: InputEvent? = null

// Use the URIs of the server-side endpoints that accept attribution source
// registration.
val sourceParam1 = WebSourceParams.Builder(Uri.parse(
        "https://adtech1.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
// True, if debugging is allowed for the ad tech.
    .setDebugKeyAllowed(true)
    .build()

val sourceParam2 = WebSourceParams.Builder(Uri.parse(
        "https://adtech2.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    .setDebugKeyAllowed(false)
    .build()

val sourceParam3 = WebSourceParams.Builder(Uri.parse(
        "https://adtech3.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    .build()

val sourceParams = Arrays.asList(sourceParam1, sourceParam2, sourceParam3)
val publisherOrigin = Uri.parse("https://publisher.example")
val appDestination = Uri.parse("android-app://com.example.store")
val webDestination = Uri.parse("https://example.com")

val future = CompletableFuture<Void>()

adView.setOnTouchListener {_: View?, event: MotionEvent? ->
    exampleClickEvent = event
    true
}
val clickRegistrationRequest = WebSourceRegistrationRequest.Builder(
          sourceParams,
          publisherOrigin)
      .setAppDestination(appDestination)
      .setWebDestination(webDestination)
      .setInputEvent(event)
      .build()
val viewRegistrationRequest = WebSourceRegistrationRequest.Builder(
          sourceParams,
          publisherOrigin)
      .setAppDestination(appDestination)
      .setWebDestination(webDestination)
      .setInputEvent(null)
      .build()

// Register a web source for a click event.
measurementManager.registerWebSource(
        clickRegistrationRequest,
        CALLBACK_EXECUTOR,
        future::complete)

// Register a web source for a view event.
measurementManager.registerWebSource(
        viewRegistrationRequest,
        CALLBACK_EXECUTOR,
        future::complete)

Java

private static final Executor CALLBACK_EXECUTOR =
        Executors.newCachedThreadPool();
private InputEvent exampleClickEvent;

MeasurementManager measurementManager =
        context.getSystemService(MeasurementManager.class);

// Use the URIs of the server-side endpoints that accept attribution source
// registration.
WebSourceParams sourceParam1 = WebSourceParams.Builder(Uri.parse(
        "https://adtech1.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    // True, if debugging is allowed for the ad tech.
    .setDebugKeyAllowed(true)
    .build();

WebSourceParams sourceParam2 = WebSourceParams.Builder(Uri.parse(
        "https://adtech2.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    .setDebugKeyAllowed(false)
    .build();

WebSourceParams sourceParam3 = WebSourceParams.Builder(Uri.parse(
        "https://adtech3.example/attribution_source?AD_TECH_PROVIDED_METADATA"))
    .build();

List<WebSourceParams> sourceParams =
        Arrays.asList(sourceParam1, sourceParam2, sourceParam3);
Uri publisherOrigin = Uri.parse("https://publisher.example");
Uri appDestination = Uri.parse("android-app://com.example.store");
Uri webDestination = Uri.parse("https://example.com");

CompletableFuture<Void> future = new CompletableFuture<>();

adView.setOnTouchListener(v, event) -> {
    exampleClickEvent = event;
    return true;
}

WebSourceRegistrationRequest clickRegistrationRequest =
        new WebSourceRegistrationRequest.Builder(sourceParams, publisherOrigin)
    .setAppDestination(appDestination)
    .setWebDestination(webDestination)
    .setInputEvent(event)
    .build();
WebSourceRegistrationRequest viewRegistrationRequest =
        new WebSourceRegistrationRequest.Builder(sourceParams, publisherOrigin)
    .setAppDestination(appDestination)
    .setWebDestination(webDestination)
    .setInputEvent(null)
    .build();

// Register a web source for a click event.
measurementManager.registerWebSource(clickRegistrationRequest,
        CALLBACK_EXECUTOR, future::complete);

// Register a web source for a view event.
measurementManager.registerWebSource(viewRegistrationRequest,
        CALLBACK_EXECUTOR, future::complete);

Ten fragment kodu pokazuje przykład wywołania interfejsu API, które przeglądarka wykonuje, aby zarejestrować konwersję po przekierowaniu użytkownika z aplikacji:

Kotlin

companion object {
    private val CALLBACK_EXECUTOR = Executors.newCachedThreadPool()
}

val measurementManager = context.getSystemService(MeasurementManager::class.java)

// Use the URIs of the server-side endpoints that accept trigger registration.
val triggerParam1 = WebTriggerParams.Builder(Uri.parse(
        "https://adtech1.example/trigger?AD_TECH_PROVIDED_METADATA"))
    // True, if debugging is allowed for the ad tech.
    .setDebugKeyAllowed(true)
    .build()

val triggerParam2 = WebTriggerParams.Builder(Uri.parse(
        "https://adtech2.example/trigger?AD_TECH_PROVIDED_METADATA"))
    .setDebugKeyAllowed(false)
    .build()

val triggerParams = Arrays.asList(triggerParam1, triggerParam2)
val advertiserOrigin = Uri.parse("https://advertiser.example")

val future = CompletableFuture<Void>()

val triggerRegistrationRequest = WebTriggerRegistrationRequest.Builder(
        triggerParams,
        advertiserOrigin)
    .build()

// Register the web trigger (conversion).
measurementManager.registerWebTrigger(
    triggerRegistrationRequest,
    CALLBACK_EXECUTOR,
    future::complete)

Java

private static final Executor CALLBACK_EXECUTOR =
        Executors.newCachedThreadPool();

MeasurementManager measurementManager =
        context.getSystemService(MeasurementManager.class);

// Use the URIs of the server-side endpoints that accept trigger registration.
WebTriggerParams triggerParam1 = WebTriggerParams.Builder(Uri.parse(
        "https://adtech1.example/trigger?AD_TECH_PROVIDED_METADATA"))
    // True, if debugging is allowed for the ad tech.
    .setDebugKeyAllowed(true)
    .build();

WebTriggerParams triggerParam2 = WebTriggerParams.Builder(Uri.parse(
        "https://adtech2.example/trigger?AD_TECH_PROVIDED_METADATA"))
    .setDebugKeyAllowed(false)
    .build();

List<WebTriggerParams> triggerParams =
        Arrays.asList(triggerParam1, triggerParam2);
Uri advertiserOrigin = Uri.parse("https://advertiser.example");

CompletableFuture<Void> future = new CompletableFuture<>();

WebTriggerRegistrationRequest triggerRegistrationRequest =
        new WebTriggerRegistrationRequest.Builder(
            triggerParams, advertiserOrigin)
    .build();

// Register the web trigger (conversion).
measurementManager.registerWebTrigger( triggerRegistrationRequest,
        CALLBACK_EXECUTOR, future::complete);

Dodawanie szumu na potrzeby prywatności

Raporty na poziomie zdarzenia zawierają dane o miejscu docelowym, identyfikatorze źródła atrybucji i wyzwalaczu. Dane są wysyłane w pierwotnym (niezaszyfrowanym) formacie do źródła raportu. Aby chronić prywatność użytkowników, można dodać szum, który utrudnia identyfikację pojedynczego użytkownika. Raporty na poziomie zdarzenia z dodanymi szumem są generowane i wysyłane zgodnie z ramami prywatności różnicowej. Oto domyślne wartości procentowe szumu w różnych scenariuszach:

Typ źródła

Wartość źródła docelowego

Prawdopodobieństwo raportu o szumieniu na podstawie rejestracji źródła

Wyświetl

Aplikacja lub przeglądarka

0,0000025

Wyświetl

Aplikacja i internet

0,0000042

Kliknięcie

Aplikacja lub przeglądarka

0,0024263

Kliknięcie

Aplikacja i internet

0,0170218

W przypadku pomiaru atrybucji „aplikacja–strona internetowa”, w którym źródła mogą generować konwersje zarówno w aplikacji, jak i w witrynie, w raportach na poziomie zdarzenia możesz określić, czy zdarzenie wywołujące miało miejsce w aplikacji czy w witrynie. Aby zrekompensować te dodatkowe szczegóły, generowane raporty z dodanym szumem mogą zawierać do 7 razy więcej kliknięć i do 1,7 raza więcej wyświetleń.

Niektóre technologie reklamowe nie wymagają raportów na poziomie zdarzenia, aby określić, czy wyzwalacz wystąpił w aplikacji czy w miejscu docelowym w internecie. Aby zmniejszyć szum, specjaliści ds. technologii reklamowych mogą użyć pola coarse_event_report_destinations w sekcji Attribution-Reporting-Register-Source. Jeśli atrybucja zostanie przypisana do źródła z wypełnionym polem coarse_event_report_destinations, raport będzie zawierać zarówno miejsca docelowe w aplikacji, jak i w witrynie, bez rozróżniania, w którym miejscu wystąpił rzeczywisty bodziec.

W tych przykładach użytkownik klika reklamę, a jej źródło jest zarejestrowane w interfejsie API. Następnie użytkownik dokonuje konwersji zarówno w aplikacji reklamodawcy, jak i w jego witrynie. Obie te konwersje są rejestrowane jako wyzwalacze i przypisywane do początkowego kliknięcia.

Nagłówek HTTP rejestracji źródła na podstawie kliknięcia:

Attribution-Reporting-Register-Source: {
    "destination": "android-app://com.advertiser.example",
    "web_destination": "https://advertiser.com",
    "source_event_id": "234",
    "expiry": "60000",
    "priority": "5",
    // Ad tech opts out of receiving app-web destination distinction
    // in event report, avoids additional noise
    "coarse_event_report_destinations": "true"
}

Aktywator jest rejestrowany z aplikacji o nazwie pakietu com.advertiser.example:

Attribution-Reporting-Register-Trigger: {
    "event_trigger_data": [{
    "trigger_data": "1",
    "priority": "1"
    }],
}

Wyzwalacz jest rejestrowany z przeglądarki w witrynie z domeną eTLD+1:https://advertiser.com

Attribution-Reporting-Register-Trigger: {
    "event_trigger_data": [{
    "trigger_data": "2",
    "priority": "2"
    }],
}

Powstałe raporty na poziomie zdarzenia są generowane. Zakładając, że oba te czynniki są przypisane do źródła, generowane są następujące raporty na poziomie zdarzenia:

  {
    "attribution_destination": ["android-app://com.advertiser.example,https://advertiser.com"],
    "scheduled_report_time": "800176400",
    "source_event_id": "53234",
    "trigger_data": "1",
    // Can be "event" if source were registered by user viewing the ad
    "source_type": "navigation",
    // Would be 0.0170218 without coarse_event_report_destinations as true in the source
    "randomized_trigger_rate": 0.0024263
  }

generować i przesyłać raporty;

Interfejs Attribution Reporting API wysyła raporty do punktów końcowych na serwerze, które akceptują raporty na poziomie zdarzeniaraporty zbiorcze.

Wymuszanie uruchomienia zadań raportowania

Gdy zarejestrujesz zdarzenie źródła atrybucji lub zdarzenie wywołania, system zaplanował uruchomienie zadania raportowania. Domyślnie to zadanie jest wykonywane co 4 godziny. Na potrzeby testów możesz wymusić uruchomienie zadań raportowania lub skrócić odstępy między nimi.

Wymuszenie uruchomienia zadania atrybucji:

adb shell cmd jobscheduler run -f com.google.android.adservices.api 5

Wymuszanie uruchomienia zadania raportowania na poziomie zdarzenia:

adb shell cmd jobscheduler run -f com.google.android.adservices.api 3

Wymuś uruchomienie zadania polegającego na wygenerowaniu raportu z możliwością agregacji:

adb shell cmd jobscheduler run -f com.google.android.adservices.api 7

Sprawdź dane wyjściowe w logcat, aby dowiedzieć się, kiedy zadania zostały uruchomione. Powinien on wyglądać mniej więcej tak:

JobScheduler: executeRunCommand(): com.google.android.adservices.api/0 5 s=false f=true

Wymuszanie dostarczania raportów

Nawet jeśli zadanie generowania raportów zostanie uruchomione przymusowo, system będzie nadal wysyłać raporty zgodnie z zaplanowanymi terminami dostarczenia, które wynoszą od kilku godzin do kilku dni. W celu przeprowadzenia testów możesz przesunąć czas na urządzeniu, aby przesunąć zaplanowane opóźnienia i zainicjować wysyłanie raportów.

Weryfikowanie raportów na serwerze

Po wysłaniu raportów sprawdź ich dostarczenie, sprawdzając otrzymane raporty i odpowiednie logi serwera, np. z serwera testowego lub z własnego systemu.

Interpretowanie raportu zbiorczego

W przypadku otrzymania raportu zbiorczego pole debug_cleartext_payload zawiera niezaszyfrowaną wersję raportu zbiorczego. Chociaż ta wersja raportu jest niezaszyfrowana, musisz ją jeszcze odkodować.

Poniżej przedstawiamy przykład dekodowania zawartości pola debug_cleartext_payload w 2 kroki: pierwszy to dekodowanie w formacie Base64, a drugi to dekodowanie w formacie CBOR.

String base64DebugPayload  = "omRkYXRhgqJldmFsdWVEAAAGgGZidWNrZXRQAAAAAAAAAAAAAAAAAAAKhaJldmFsdWVEAACAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAFWWlvcGVyYXRpb25paGlzdG9ncmFt";
byte[] cborEncoded = Base64.getDecoder().decode(base64DebugPayload);

// CbodDecoder comes from this library https://github.com/c-rack/cbor-java
final List<DataItem> dataItems = new CborDecoder(new ByteArrayInputStream(cborEncoded)).decode();

// In here you can see the contents, but the value will be something like:
// Data items: [{ data: [{ value: co.nstant.in.cbor.model.ByteString@a8b5c07a,
//   bucket: co.nstant.in.cbor.model.ByteString@f812097d },
//   { value: co.nstant.in.cbor.model.ByteString@a8b5dfc0,
//   bucket: co.nstant.in.cbor.model.ByteString@f8120934 }], operation: histogram }]
Log.d("Data items : " + dataItems);

// In order to see the value for bucket and value, you can traverse the data
// and get their values, something like this:
final Map payload = (Map) dataItems.get(0);
final Array payloadArray = (Array) payload.get(new UnicodeString("data"));

payloadArray.getDataItems().forEach(i -> {
    BigInteger value = new BigInteger(((ByteString) ((Map)i).get(new UnicodeString("value"))).getBytes());
    BigInteger bucket = new BigInteger(((ByteString) ((Map)i).get(new UnicodeString("bucket"))).getBytes());
    Log.d("value : " + value + " ;bucket : " + bucket);
});

Testowanie

Aby rozpocząć korzystanie z interfejsu Attribution Reporting API, możesz użyć projektu MeasurementSampleApp na GitHubie. Ta przykładowa aplikacja demonstruje rejestrację źródła atrybucji i rejestrację reguły.

W przypadku punktów końcowych serwera zapoznaj się z tymi materiałami referencyjnymi lub z własnym rozwiązaniem:

  • MeasurementAdTechServerSpec zawiera definicje usług OpenAPI, które można wdrożyć na obsługiwanych platformach mock lub mikroserwisów.
  • MeasurementAdTechServer zawiera referencyjne wdrożenie serwera testowego opartego na aplikacji Spring Boot dla Google App Engine.

Wymagania wstępne

Wdróż symulowane interfejsy API na zdalnych punktach końcowych dostępnych z urządzenia testowego lub emulatora. Aby ułatwić sobie testowanie, zapoznaj się z przykładowymi projektami MeasurementAdTechServerSpecMeasurementAdTechServer.

Funkcje do przetestowania

  • Wykonaj rejestracje źródła atrybucji i konwersji. Sprawdź, czy punkty końcowe po stronie serwera odpowiadają w prawidłowym formacie.
  • Wykonywanie zadań raportowania.
  • Sprawdź dostarczanie raportów na zapleczu serwera testowego lub w konsoli.

Nadchodzące funkcje

Elastyczna konfiguracja na poziomie zdarzenia

Konfiguracja domyślna raportowania na poziomie zdarzenia jest zalecana na potrzeby rozpoczęcia testowania użyteczności, ale może nie być odpowiednia w przypadku wszystkich przypadków użycia. Interfejs Attribution Reporting API będzie obsługiwać opcjonalne, bardziej elastyczne konfiguracje, dzięki czemu firmy zajmujące się technologiami reklamowymi będą mieć większą kontrolę nad strukturą raportów na poziomie zdarzenia i będą mogły w pełni wykorzystać potencjał tych danych. Ta dodatkowa elastyczność zostanie wprowadzona w Attribution Reporting API w 2 fazach:

  • Etap 1: elastyczna konfiguracja na poziomie zdarzenia w wersji lite; podzbiór etapów 2.
  • Etap 2: pełna wersja elastycznej konfiguracji na poziomie zdarzenia.

Etap 1. Elastyczny poziom zdarzeń typu lite

Do pliku JSON w polu Attribution-Reporting-Register-Source dodamy 2 parametry opcjonalne:

  • max_event_level_reports
  • event_report_windows
{
  ...
  // Optional. This is a parameter that acts across all trigger types for the
  // lifetime of this source. It restricts the total number of event-level
  // reports that this source can generate. After this maximum is hit, the
  // source is no longer capable of producing any new data. The use of
  // priority in the trigger attribution algorithm in the case of multiple
  // attributable triggers remains unchanged. Defaults to 3 for navigation
  // sources and 1 for event sources
  "max_event_level_reports": <int>,

  // Optional. Represents a series of time windows, starting at 0. Reports
  // for this source will be delivered an hour after the end of each window.
  // Time is encoded as seconds after source registration. If
  // event_report_windows is omitted, will use the default windows. This
  // field is mutually exclusive with the existing `event_report_window` field.
  // // End time is exclusive.
  "event_report_windows": {
    "start_time": <int>,
    "end_times": [<int>, ...]
  }
}

Przykład konfiguracji niestandardowej

Ta przykładowa konfiguracja ułatwia optymalizację otrzymywania raportów w wcześniejszych okresach raportowania.

{
  ...
  "max_event_level_reports": 2,
  "event_report_windows": {
    "end_times": [7200, 43200, 86400] // 2 hours, 12 hours, 1 day in seconds
  }
}

Etap 2. Pełny elastyczny poziom zdarzenia

Oprócz parametrów dodanych w etap 1 do pliku JSON w pliku Attribution-Reporting-Register-Source dodamy dodatkowy opcjonalny parametr trigger_specs.

{
  // A trigger spec is a set of matching criteria, along with a scheme to
  // generate bucketized output based on accumulated values across multiple
  // triggers within the specified event_report_window. There will be a limit on
  // the number of specs possible to define for a source.
  "trigger_specs": [{
    // This spec will only apply to registrations that set one of the given
    // trigger data values (non-negative integers) in the list.
    // trigger_data will still appear in the event-level report.
    "trigger_data": [<int>, ...]

    // Represents a series of time windows, starting at the source registration
    // time. Reports for this spec will be delivered an hour after the end of
    // each window. Time is encoded as seconds after source registration.
    // end_times must consist of strictly increasing positive integers.
    //
    // Note: specs with identical trigger_data cannot have overlapping windows;
    // this ensures that triggers match at most one spec. If
    // event_report_windows is omitted, will use the "event_report_window" or
    // "event_report_windows" field specified at the global level for the source
    // (or the default windows if none are specified). End time is exclusive.
    "event_report_windows": {
      "start_time": <int>,
      "end_times": [<int>, ...],
    }

    // Represents an operator that summarizes the triggers within a window
    // count: number of triggers attributed within a window
    // value_sum: sum of the value of triggers within a window
    // The summary is reported as an index into a bucketization scheme. Defaults
    // to "count"
    "summary_window_operator": <one of "count" or "value_sum">,

    // Represents a bucketization of the integers from [0, MAX_INT], encoded as
    // a list of integers where new buckets begin (excluding 0 which is
    // implicitly included).
    // It must consist of strictly increasing positive integers.
    //
    // e.g. [5, 10, 100] encodes the following ranges:
    // [[0, 4], [5, 9], [10, 99], [100, MAX_INT]]
    //
    // At the end of each reporting window, triggers will be summarized into an
    // integer which slots into one of these ranges. Reports will be sent for
    // every new range boundary that is crossed. Reports will never be sent for
    // the range that includes 0, as every source is initialized in this range.
    //
    // If omitted, then represents a trivial mapping
    // [1, 2, ... , MAX_INT]
    // With MAX_INT being the maximum int value defined by the browser.
    "summary_buckets": [<bucket start>, ...]
  }, {
    // Next trigger_spec
  } ...],

  // See description in phase 1.
  "max_event_level_reports": <int>
  // See description in phase 1.
  "event_report_windows": {
    "start_time": <int>,
    "end_times": [<int>, ...]
  }
}

Ta konfiguracja w pełni określa przestrzeń wyjściową raportów na poziomie zdarzenia według rejestracji źródła. W przypadku każdej specyfikacji reguły określamy:

  • Zestaw kryteriów dopasowania:
    • Do których danych o wyzwalaczu ma zastosowanie ta specyfikacja. To źródło może być dopasowywane tylko do reguł, które mają jedną ze wskazanych wartości trigger_data w polu trigger_specs. Inaczej mówiąc, jeśli reguła pasuje do tego źródła, ale jej parametr trigger_data nie jest jedną z wartości w konfiguracji źródła, reguła jest ignorowana.
    • Gdy określony przez Ciebie reguła spełnia te kryteria (za pomocą funkcji event_report_windows). Pamiętaj, że reguła może nadal pasować do źródła raportów podlegających agregacji, mimo że nie spełnia 2 wymienionych wcześniej kryteriów dopasowania.
  • Specjalny algorytm służący do podsumowywania i grupowania wszystkich wyzwalaczy w oknie atrybucji. Dzięki temu w przypadku reguł można określić parametr value, który jest sumowany dla określonej specyfikacji, ale raportowany jako wartość zbiorcza.

Będą one też obsługiwać dodawanie parametru opcjonalnej wartości w słownikach w sekcji event_trigger_data.

{
  "event_trigger_data": [
    {
      "trigger_data": "2",
      "value": 100,  // Defaults to 1
      "filters": ...
    },
    ...
  ]
}

Każda rejestracja reguły będzie pasować do maksymalnie 1 specyfikacji reguły i zaktualizuje powiązaną z nią wartość podsumowania. Ogólnie rzecz biorąc, w momencie uruchomienia:

  • Zastosuj globalne filtry atrybucji.
  • W przypadku każdej specyfikacji reguły sprawdź wartość event_trigger_data w specyfikacji, aby znaleźć dopasowanie, używając event_reporting_window w specyfikacji. Pole najwyższego poziomu event_reporting_windows działa jako wartość domyślna w przypadku, gdy specyfikacja reguły to brakujące podpole event_report_windows.
  • Do atrybucji jest wybierana pierwsza pasująca specyfikacja, a wartość podsumowania jest zwiększana o value.

Gdy event_report_window dla specyfikacji zostanie ukończone, jej wartość sumaryczna zostanie przypisana do zbioru, a my wyślemy raport na poziomie zdarzenia dla każdego przyrostu w zbiorze sumarycznym spowodowanego przypisanymi wartościami reguły. Raporty będą zawierać 1 dodatkowe pole: trigger_summary_bucket.

{
  ...
  "trigger_summary_bucket": [<bucket start>, <bucket end>],
}

konfiguracje, które są równoważne bieżącej wersji;

Poniżej znajdziesz odpowiednie konfiguracje dla interfejsów API odpowiadających bieżącym zdarzeniom i źródłom nawigacji. Pokazuje to, dlaczego poziomy szumu są tak wysokie w przypadku źródeł nawigacji w porównaniu ze źródłami zdarzeń, które mają te same wartości epsilona: źródła nawigacji mają znacznie większą przestrzeń wyjściową.

Możliwe, że istnieje wiele równoważnych konfiguracji, ponieważ niektóre parametry mogą być ustawione jako domyślne lub wykluczone.

Równoważne źródła zdarzeń
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1],
    "event_report_windows": {
      "end_times": [<30 days>]
    },
    "summary_window_operator": "count",
    "summary_buckets": [1],
  }],
  "max_event_level_reports": 1,
  ...
  // expiry must be greater than or equal to the last element of the end_times
  "expiry": <30 days>,
}
Źródła nawigacji o równoważnych funkcjach
// Note: most of the fields here are not required to be explicitly listed.
// Here we list them explicitly just for clarity.
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1, 2, 3, 4, 5, 6, 7],
    "event_report_windows": {
      "end_times": [<2 days>, <7 days>, <30 days>]
    },
    "summary_window_operator": "count",
    "summary_buckets": [1, 2, 3],
  }],
  "max_event_level_reports": 3,
  ...
  // expiry must be greater than or equal to the last element of the end_times
  "expiry": <30 days>,
}

Przykładowe konfiguracje niestandardowe

Poniżej znajdziesz kilka dodatkowych konfiguracji poza domyślnymi. We wszystkich tych przykładach kompromisy dla deweloperów obejmują:

  • zmniejszenie niektórych wymiarów konfiguracji domyślnej (liczba reguł, moc zbioru danych reguły, liczba okien) w celu zwiększenia innego wymiaru, aby zachować poziom szumu;
  • zmniejszenie niektórych wymiarów domyślnej konfiguracji (np. liczby wyzwalaczy, mocy danych wyzwalacza, liczby okien) w celu zmniejszenia poziomu szumu;

Raportowanie zakresów wartości aktywatora

Ta przykładowa konfiguracja umożliwia deweloperowi optymalizację pod kątem wartości danych tylko z jednego okna raportowania (np. 7 dni), co pozwala ograniczyć ilość szumu. W tym przykładzie każdy element reguły, który ustawia wartość trigger_data na wartość inną niż 0, nie kwalifikuje się do przypisania.

{
  "trigger_specs": [
  {
    "trigger_data": [0],
    "event_report_windows": {
      "end_times": [604800, 1209600] // 7 days, 14 days represented in seconds
    },
    "summary_window_operator": "value_sum",
    "summary_buckets": [5, 10, 100]
  }],
}

Wyzwalacze mogą być rejestrowane za pomocą zestawu pól value, które są sumowane i grupowane. Jeśli np. w ciągu 7 dni od rejestracji źródła wystąpiły 3 wyzwalacze o wartościach 1, 3 i 4.

{ "event_trigger_data": [{"trigger_data": "0", "value": 1}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 3}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 4}] }

Wartości są sumowane do 8 i raportowane w tych raportach po 7 dniach + 1 godzinie:

// Report 1
{
  ...
  "trigger_summary_bucket": [5, 9]
}

W ciągu kolejnych 7 dni zarejestrowane są te reguły:

{ "event_trigger_data": [{"trigger_data": "0", "value": 50}] }
{ "event_trigger_data": [{"trigger_data": "0", "value": 45}] }

Wartości sumują się do 8 + 50 + 45 = 103. W wyniku tego uzyskasz te raporty: 14 dni + 1 godzina:

// Report 2
{
  ...
  "trigger_summary_bucket": [10, 99]
},

// Report 3
{
  ...
  "trigger_summary_bucket": [100, MAX_INT]
}
Raportowanie liczby reguł

Ten przykład pokazuje, jak deweloper może skonfigurować źródło, aby uzyskać liczbę uruchamień do 10.

{
  "trigger_specs": [
  {
    "trigger_data": [0],
    "event_report_windows": {
      "end_times": [604800] // 7 days represented in seconds
    },
    // This field could be omitted to save bandwidth since the default is "count"
    "summary_window_operator": "count",
    "summary_buckets": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  }],
}

Przypisane wyzwalacze z wartością trigger_data = 0 są zliczane i ograniczone do 10. Wartość reguły jest ignorowana, ponieważ summary_window_operator ma ustawienie „liczba”. Jeśli zarejestrowane są 4 wyzwalacze i przypisane do źródła, raport będzie wyglądał tak:

// Report 1
{
  ...
  "trigger_summary_bucket": [1, 1]
}
// Report 2
{
  ...
  "trigger_summary_bucket": [2, 2]
}
// Report 3
{
  ...
  "trigger_summary_bucket": [3, 3]
}
// Report 4
{
  ...
  "trigger_summary_bucket": [4, 4]
}
binarne z częstszym raportowaniem,

Ta przykładowa konfiguracja ułatwia życie deweloperowi, który chce się dowiedzieć, czy w ciągu pierwszych 10 dni wystąpiła co najmniej 1 konwersja (niezależnie od jej wartości), ale chce otrzymywać raporty w częstszych odstępach czasu niż domyślne. Ponownie w tym przykładzie każdy wyzwalacz, który ustawia wartość trigger_data na wartość inną niż 0, nie kwalifikuje się do przypisania. Dlatego ten przypadek użycia nazywamy binarnym.

{
  "trigger_specs": [
  {
    "trigger_data": [0],
    "event_report_windows": {
      // 1 day, 2 days, 3 days, 5 days, 7 days, 10 days represented in seconds
      "end_times": [86400, 172800, 259200, 432000, 604800, 864000]
    },
    // This field could be omitted to save bandwidth since the default is "count"
    "summary_window_operator": "count",
    "summary_buckets": [1]
  }],
}
zmienić specyfikacje aktywatora w zależności od źródła;
{
  "trigger_specs": [
  {
    "trigger_data": [0, 1, 2, 3],
    "event_report_windows": {
      "end_times": [172800, 604800, 2592000] // 2 days, 7 days, 30 days represented in seconds
    }
  }],
  "max_event_level_reports": 3
}
{
  "trigger_specs": [
  {
    "trigger_data": [4, 5, 6, 7],
    "event_report_windows": {
      "end_times": [172800, 604800, 2592000] // 2 days, 7 days, 30 days represented in seconds
    }
  }],
  "max_event_level_reports": 3
}

Zachęcamy deweloperów do sugerowania różnych zastosowań tego rozszerzenia interfejsu API. Na tej podstawie zaktualizujemy ten artykuł o przykładowe konfiguracje dla tych zastosowań.

Atrybucja międzysieciowa bez przekierowań

Technologie reklamowe powinny używać przekierowań do rejestrowania wielu źródeł atrybucji oraz do przeprowadzania atrybucji między sieciami. Ta funkcja pomaga obsługiwać atrybucję w wielu sieciach, gdy przekierowania nie są możliwe w różnych sieciach. Więcej informacji

Firmy dostarczające technologie reklamowe mogą wysyłać konfigurację w odpowiedzi na wywołanie rejestracji na podstawie tego, które źródła zarejestrowane przez inne firmy dostarczające technologie reklamowe są wybierane do generowania pochodnych źródeł. Te pochodne źródła są następnie używane do atrybucji. Raporty zbiorcze są generowane, jeśli wyzwalacz zostanie przypisany do źródła pochodzenia. Generowanie raportów zdarzeń w przypadku źródeł pochodnych nie jest obsługiwane.

Dostawcy technologii reklamowych mogą wybrać aggregation_keys w swoich zarejestrowanych źródłach, które chcą udostępnić partnerom. Te klucze można zadeklarować w opcjonalnym polu shared_aggregation_keys, które znajduje się pod nagłówkiem Attribution-Reporting-Register-Source rejestracji źródła:

"shared_aggregation_keys": ["[key name1]", "[key name2]"]

Źródła pochodne są generowane na podstawie konfiguracji w nagłówku rejestracji reguły Attribution-Reporting-Register-Trigger:

  // Specifies the configuration based on which derived sources should be
  // generated. Those derived sources will be included for source matching at the
  // time of attribution. For example, if adtech2 is registering a trigger with an
  // attribution_config with source_network as adtech1, available sources
  // registered by adtech1 will be considered with additional filtering criteria
  // applied to that set as mentioned in the attribution_config. Derived
  // sources can have different values to priority, post_install_exclusivity_window
  // etc.

  "attribution_config": [
    {
      // Derived sources are created from this adtech's registered sources
      "source_network": "[original source's adtech enrollment ID]",
      //(optional) Filter sources whose priority falls in this range
      "source_priority_range": {
        "start": [priority filter lower bound],
        "end": [priority filter upper bound]
      },
      // (optional) Filter sources whose at least one of filter maps matches these
      // filters
      "source_filters": {
        "key name 1": ["key1 value 1"]
      },
      // (optional) Filter sources whose none of filter map matches these
      // filters
        "source_not_filters": {
          "key name 1": ["key1 value 1"]
        },
      // (optional) Apply this priority to the generated derived sources
      "priority": "[64 bit signed integer]",
      // (optional) The derived source will have expiry set as this or parent
      // source's, whichever is earlier
      "expiry": "[64 bit signed integer]",
      // (optional) set on the derived source
      "filter_data": {
        "key name 1": ["key1 value 1"]
      },
      // (optional) set on the derived source
      "post_install_exclusivity_window": "[64-bit unsigned integer]"
    }
  ]

Oto wersja z dodanymi przykładowymi wartościami:

  "attribution_config": [
    {
      "source_network": "adtech1-enrollment-id",
      "source_priority_range": {
        "start": 50,
        "end": 100
      },
      "source_filters": {
        "source_type": ["NAVIGATION"]
      },
      "source_not_filters": {
        "product_id": ["789"]
      },
      "priority": "30",
      "expiry": "78901",
      // (optional) set on the derived source
      "filter_data": {
        "product_id": ["1234"]
        },
      // (optional) set on the derived source
      "post_install_exclusivity_window": "7890"
    }
  ]

Do nagłówka wywołania rejestracji dodano 2 nowe pola opcjonalne. Te pola umożliwiają użycie identyfikatora zwycięskiej firmy dostarczającej technologię reklamową w kluczach raportów umożliwiających ich agregację:

  • x_network_bit_mapping: mapowanie identyfikatora rejestracji na bit identyfikatora technologii reklamowej
  • x_network_data: przesunięcie (w lewo) dla zwycięskiej technologii reklamowej x_network_bit_mapping LUB operacja z kluczem uruchamiającym
Przykład:
"Attribution-Reporting-Register-Trigger": {
  "attribution_config": [...],
  "aggregatable_trigger_data": [
    {
     "key_piece": "0x400",
     "source_keys": ["campaignCounts"]
      "x_network_data" : {
        "key_offset" : 12 // [64 bit unsigned integer]
      }
    }
    
  ]
  
  "x_network_bit_mapping": {
   // This mapping is used to generate trigger key pieces with AdTech identifier
   // bits. eg. If AdTechA's sources wins the attribution then 0x1 here will be
   // OR'd with the trigger key pieces to generate the final key piece.
    "AdTechA-enrollment_id": "0x1", // Identifier bits in hex for A
    "AdTechB-enrollment_id": "0x2"  // Identifier bits in hex for B
  }
  
}

Oto wynik obliczenia elementu klucza reguły podczas generowania raportu dla źródła AdTechB:

  • key_piece: 0x400 (010000000000)
  • key_offset: 12
  • Wartość enrollment_id w AdtechB: 2 (010) (z x_network_bit_mapping)
  • Wynikowy element klucza reguły: 0x400 | 0x2 << 12 = 0x2400

Ograniczenia

Listę rozwijających się funkcji środowiska wykonawczego pakietu SDK znajdziesz w informacjach o wersji.

Zgłaszanie błędów i problemów

Twoja opinia jest bardzo ważna dla Piaskownicy prywatności na Androida. Poinformuj nas o jakichkolwiek problemach lub pomysłach na ulepszenie Piaskownicy prywatności na Androida.