Przewodnik dla programistów interfejsu Attribution Reporting API

Podczas lektury dokumentacji Piaskownicy prywatności na Androida użyj przycisku Podgląd dla programistów lub Beta, aby wybrać wersję programu, z której korzystasz (instrukcje mogą się różnić).


Prześlij opinię

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 użycia atrybucji i pomiaru konwersji w różnych aplikacjach. W tym przewodniku dla programistów opisujemy, jak konfigurować i testować interfejsy Attribution Reporting API w celu rejestrowania kliknięć, obejrzeń i konwersji reklam za pomocą metod, które rejestrują odpowiednie reguły i źródła tych zdarzeń.

Z tego przewodnika dowiesz się, jak skonfigurować punkty końcowe serwera i utworzyć aplikację kliencką, która będzie wywoływać te usługi. Więcej informacji o ogólnym wyglądzie interfejsu Attribution Reporting API znajdziesz w ofercie pakietowej.

Kluczowe terminy

  • Źródła atrybucji odnoszą się do kliknięć lub wyświetleń.
  • Reguły to zdarzenia, które można przypisać do konwersji.
  • Raporty zawierają dane o aktywatorze i odpowiadającym mu źródle atrybucji. Raporty te są wysyłane w odpowiedzi na zdarzenia aktywujące. Interfejs Attribution Reporting API obsługuje raporty na poziomie zdarzenia i raporty zbiorcze.

Zanim zaczniesz

Aby korzystać z interfejsu Attribution Reporting API, wykonaj zadania po stronie serwera i klienta wymienione w sekcjach poniżej.

Konfigurowanie punktów końcowych interfejsu Attribution Reporting API

Interfejs Attribution Reporting API wymaga zbioru punktów końcowych, do których masz dostęp za pomocą urządzenia testowego lub emulatora. Utwórz po jednym punkcie końcowym dla każdego z tych zadań po stronie serwera:

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

  • Najszybszym sposobem na rozpoczęcie korzystania z usługi jest wdrożenie definicji usług OpenAPI w wersji 3 z naszego przykładowego repozytorium kodu na przykładowej platformie lub platformie mikroserwisów. Możesz użyć platformy Postman, Prism lub innej fałszywej platformy serwera, która akceptuje ten format. Wdróż każdy punkt końcowy i śledź identyfikatory URI do wykorzystania w aplikacji. Aby sprawdzić, czy raport jest dostarczany, odnieś się do wywołań wcześniej wykonywanych na platformie demonstracyjnej lub bezserwerowej.
  • Uruchom własny samodzielny serwer, korzystając z przykładu z kotlin opartego na wiosennym rozruchu. Wdróż ten serwer u dostawcy chmury lub w infrastrukturze wewnętrznej.
  • Użyj definicji usług jako przykładów do zintegrowania punktów końcowych z istniejącym systemem.

Zaakceptuj rejestrację źródła

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

https://adtech.example/attribution_source

Gdy aplikacja kliencka rejestruje źródło atrybucji, podaje identyfikator URI tego punktu końcowego serwera. Attribution Reporting API następnie wykonuje żądanie i zawiera jeden z tych nagłówków:

  • W przypadku zdarzeń kliknięcia:

    Attribution-Reporting-Source-Info: navigation
    
  • W przypadku zdarzeń związanych z wyświetlaniem:

    Attribution-Reporting-Source-Info: event
    

Skonfiguruj punkt końcowy serwera tak, 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 element Attribution-Reporting-Redirects zawiera identyfikatory URI partnerów z branży technologii reklamowych, interfejs Attribution Reporting API wysyła podobne żądanie do każdego z nich. Każdy partner w zakresie technologii reklamowych musi skonfigurować serwer, który odpowiada z takimi 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.

Zaakceptuj rejestrację reguły konwersji

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

https://adtech.example/attribution_trigger

Gdy aplikacja kliencka zarejestruje zdarzenie aktywatora, dostarcza identyfikator URI tego punktu końcowego serwera. Attribution Reporting API następnie wysyła żądanie i zawiera jeden z tych nagłówków:

Skonfiguruj punkt końcowy serwera tak, 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

Obowiązuje limit 25 bajtów na identyfikator klucza agregacji i ciąg filtra. Oznacza to, że identyfikatory kluczy agregacji i ciągi filtrów nie powinny mieć więcej niż 25 znaków. W tym przykładzie campaignCounts ma 14 znaków, więc jest to prawidłowy identyfikator klucza agregacji, a 1234 to 4 znaki, więc jest to prawidłowy ciąg filtra. Jeśli identyfikator klucza agregacji lub ciąg filtra przekracza 25 znaków, aktywator jest ignorowany.

Jeśli element Attribution-Reporting-Redirect zawiera identyfikatory URI partnerów z branży technologii reklamowych, interfejs Attribution Reporting API wysyła podobne żądanie do każdego z nich. Każdy partner w zakresie technologii reklamowych musi skonfigurować serwer, który odpowiada z takimi 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ć adresowany za pomocą identyfikatora URI. Więcej informacji o rejestrowaniu identyfikatorów URI znajdziesz w artykule Rejestrowanie konta Piaskownicy prywatności. (Identyfikator URI jest wywnioskowany na podstawie pochodzenia serwerów używanych do akceptowania rejestracji źródła i rejestracji aktywatorów). W przypadku przykładowych identyfikatorów URI punktów końcowych, które akceptują rejestrację źródłową i akceptują rejestrację aktywatorów, identyfikator URI tego punktu końcowego to:

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

Skonfiguruj ten serwer, aby akceptował żądania JSON w 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 dają dostęp do dodatkowych informacji w raportach atrybucji. Dowiedz się więcej o ich konfigurowaniu.

Akceptowanie raportów zbiorczych

Ten punkt końcowy powinien być adresowany za pomocą identyfikatora URI. Więcej informacji o rejestrowaniu identyfikatorów URI znajdziesz w artykule Rejestrowanie konta Piaskownicy prywatności. (Identyfikator URI jest wywnioskowany na podstawie pochodzenia serwerów używanych do akceptowania rejestracji źródła i rejestracji aktywatorów). W przypadku przykładowych identyfikatorów URI punktów końcowych, które akceptują rejestrację źródłową i akceptują rejestrację aktywatorów, identyfikator URI tego punktu końcowego to:

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

W przypadku raportów zbiorczych dostępne są zarówno zaszyfrowane, jak i niezaszyfrowane pola. Zaszyfrowane raporty umożliwiają rozpoczęcie testowania za pomocą usługi agregacji, a pole niezaszyfrowane zapewnia wgląd w strukturę danych rozumianych przez ustawione pary klucz-wartość.

Skonfiguruj ten serwer, aby akceptował żądania JSON w 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 dają dostęp do dodatkowych informacji w raportach atrybucji. Dowiedz się więcej o ich konfigurowaniu.

Skonfiguruj klienta na Androida

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

  1. Skonfiguruj środowisko programistyczne Piaskownicy prywatności na Androida.
  2. Zainstaluj obraz systemu na obsługiwanym urządzeniu lub skonfiguruj emulator obsługujący Piaskownicę prywatności na urządzeniach z Androidem.
  3. Włącz dostęp do interfejsu Attribution Reporting API, uruchamiając to polecenie ADB. (Domyślnie ten interfejs API jest wyłączony).

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

    adb shell device_config put adservices disable_measurement_enrollment_check "true"
    
  5. Aby korzystać z interfejsów Attribution Reporting API, dodaj uprawnienie ACCESS_ADSERVICES_ATTRIBUTION w pliku manifestu Androida i utwórz konfigurację usług reklamowych dla swojej aplikacji:

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

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
    
  7. Utwórz odniesienie do konfiguracji usług reklamowych w elemencie <application> 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ę plik manifestu, np. res/xml/ad_services_config.xml. Dowiedz się więcej o uprawnieniach w usługach reklamowych i kontroli dostępu do pakietów SDK.

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

Zarejestruj zdarzenia reklamowe

Aplikacja powinna rejestrować źródła i konwersje na bieżąco, aby mieć pewność, że są prawidłowo raportowane. Klasa MeasurementManager obejmuje metody, które pomagają rejestrować zdarzenia źródła atrybucji i reguły konwersji.

Rejestrowanie zdarzenia źródła atrybucji

Gdy użytkownik wyświetli lub kliknie reklamę, aplikacja wydawcy wywołuje metodę registerSource(), by zarejestrować źródło atrybucji, które jest widoczne we fragmencie kodu.

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

  • Kliknięcia, które zwykle rejestrujesz w metodzie wywołania zwrotnego podobnej do onClick(). Odpowiednie zdarzenie aktywujące zwykle ma miejsce wkrótce po kliknięciu. Ten rodzaj zdarzenia dostarcza więcej informacji o interakcji użytkownika, dlatego jest dobrym źródłem atrybucji, do którego nadaje się wysoki priorytet.
  • Widoków, które zwykle rejestruje się w metodzie wywołania zwrotnego podobnej do onAdShown(). Odpowiednie zdarzenie aktywujące może wystąpić kilka godzin lub dni po wyświetleniu zdarzenia.

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, expiry i source_priority.

Jeśli źródłowa technologia reklamowa chce udostępnić rejestracje źródeł, pierwotny identyfikator URI źródła atrybucji może zawierać przekierowania do innych punktów końcowych technologii reklamowych. Ograniczenia i reguły dotyczące przekierowań znajdziesz w ofercie technicznej.

Dodaliśmy obsługę przekierowań łańcucha połączeń w registerSource i registerTrigger. Oprócz nagłówka rejestracji konsument interfejsu API może teraz przekazywać przekierowanie HTTP w odpowiedzi serwera z kodem stanu 302 i nagłówkiem „Location” z następnym adresem URL, który umożliwia dodatkową rejestrację.

W łańcuchu połączeń wykorzystane jest tylko pole „Miejsce docelowe” podane podczas pierwszej wizyty. Liczba wizyt ma taki sam limit jak w przypadku nagłówków „Attribution-Reporting-Redirect”. Obsługa przekierowania jest uzupełnieniem obecnej funkcji „Attribution-Reporting-Redirect” (Przekierowanie raportowania atrybucji). Jeśli oba typy są dostępne, priorytetem będzie „Attribution-Reporting-Redirect” (Przekierowanie raportowania atrybucji).

Rejestrowanie zdarzenia aktywującego konwersję

Aby zarejestrować zdarzenie reguły konwersji, wywołaj w aplikacji funkcję registerTrigger():

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 raportów zdarzeń i raportów zbiorczych.

Jeśli źródłowa platforma technologii reklamowych zezwala na udostępnianie rejestracji reguł, identyfikator URI może zawierać przekierowania do identyfikatorów URI należących do innych platform technologii reklamowych. Szczegółowe informacje o ograniczeniach i regułach związanych z przekierowaniami znajdziesz w ofercie technicznej.

Rejestrowanie pomiaru w różnych aplikacjach i witrynie

Jeśli na drodze użytkownika od źródła do aktywatora korzysta zarówno aplikacja, jak i przeglądarka, występują drobne różnice w implementacji rejestrowania zdarzeń reklamowych. Jeśli użytkownik widzi reklamę w aplikacji i przekierowuje go do przeglądarki, aby dokonać konwersji, źródło konwersji jest rejestrowane przez aplikację, a konwersja – przez przeglądarkę. Podobnie jeśli użytkownik uruchamia się w przeglądarce i trafia do aplikacji w celu konwersji, przeglądarka rejestruje źródło, a aplikacja rejestruje konwersję.

Ze względu na różnice w sposobie organizacji technologii reklamowych w internecie i na Androidzie dodaliśmy nowe interfejsy API służące do rejestrowania źródeł i aktywatorów w przypadku ich działania w przeglądarkach. Główna różnica między tymi interfejsami API a odpowiadającymi im interfejsami API opartymi na aplikacjach polega na tym, że oczekujemy, że przeglądarka będzie podążać za przekierowaniami, stosować dowolne filtry specyficzne dla przeglądarki i przekazywać prawidłowe rejestracje na platformie, wywołując metodę registerWebSource() lub registerWebTrigger().

Poniższy fragment kodu przedstawia przykładowe wywołanie interfejsu API, które przeglądarka rejestruje w celu zarejestrowania źródła atrybucji przed skierowaniem 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);

Poniższy fragment kodu zawiera przykład wywołania interfejsu API, które przeglądarka rejestruje w celu zarejestrowania konwersji po skierowaniu 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);

Szum w zakresie prywatności

Raporty na poziomie zdarzenia zawierają dane o miejscu docelowym, identyfikatorze źródła atrybucji i reguły. Są one wysyłane do źródła raportowania w oryginalnym (niezaszyfrowanym) formacie. Aby chronić prywatność użytkowników, dodajemy szum utrudniający identyfikację poszczególnych użytkowników. Raporty na poziomie zdarzenia o hałasie są generowane i wysyłane zgodnie z zasadami dotyczącymi prywatności różnicowej. Oto domyślne wartości procentowe hałasu w różnych scenariuszach:

Typ źródła

Źródłowa wartość miejsca docelowego

Zaszumowane prawdopodobieństwo w raporcie według rejestracji źródła

Wyświetl

W aplikacji lub na stronie

0,0000025

Wyświetl

Aplikacja i internet

0,0000042

Kliknij

W aplikacji lub na stronie

0,0024263

Kliknij

Aplikacja i internet

0,0170218

W przypadku pomiaru atrybucji z aplikacji do witryny, gdzie źródła mogą generować konwersje zarówno do miejsc docelowych w aplikacjach, jak i w internecie, raporty na poziomie zdarzenia mogą określać, czy reguła wystąpiła w aplikacji czy na stronie. Aby to zrekompensować, generowane raporty z dźwiękiem są nawet 7-krotnie większej liczby kliknięć i około 1,7 raza w przypadku wyświetleń.

Niektórzy technologie reklamowe nie wymagają raportów na poziomie zdarzenia, aby określić, czy reguła wystąpiła w miejscu docelowym w aplikacji lub witrynie. Technologie reklamowe mogą używać pola coarse_event_report_destinations pod nagłówkiem Attribution-Reporting-Register-Source, aby ograniczyć szum. Jeśli źródło z określonym polem coarse_event_report_destinations wygrało atrybucję, wygenerowany raport będzie obejmował miejsca docelowe zarówno w aplikacji, jak i w internecie, bez rozróżniania miejsca, w którym wystąpiła rzeczywista reguła.

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

Nagłówek HTTP rejestracji źródła oparty na kliknięciach:

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 poziomu aplikacji o nazwie pakietu com.advertiser.example:

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

Aktywator jest rejestrowany przez przeglądarkę z witryny w domenie eTLD+1 https://advertiser.com:

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

Zostanie wygenerowane raporty na poziomie zdarzenia. Zakładając, że obie reguły zostaną przypisane do źródła, zostaną wygenerowane te 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
  }

Generowanie i dostarczanie raportów

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

Wymuszaj uruchamianie zadań raportowania

Gdy zarejestrujesz zdarzenie źródła atrybucji lub zdarzenie aktywujące, system zaplanuje uruchamianie zadania raportowania. Domyślnie to zadanie uruchamia się co 4 godziny. Do celów testowych możesz wymusić uruchamianie zadań raportowania lub skrócić odstępy między zadaniami.

Wymuś uruchomienie zadania atrybucji:

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

Wymuszaj uruchamianie zadania raportowania na poziomie zdarzenia:

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

Wymuszaj uruchamianie zadania raportowania możliwego do agregacji:

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

Sprawdź dane wyjściowe w logcat, aby zobaczyć, kiedy zadania zostały uruchomione. Powinno to wyglądać mniej więcej tak:

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

Wymuszanie dostarczania raportów

Nawet wtedy, gdy uruchomienie zadania raportowania jest wymuszane, system nadal wysyła raporty zgodnie z zaplanowanym czasem dostawy, który wynosi od kilku godzin do kilku dni. Na potrzeby testów możesz przesunąć czas systemowy urządzenia tak, aby był późniejszy od zaplanowanych opóźnień do rozpoczęcia wysyłania raportu.

Weryfikowanie raportów na serwerze

Po wysłaniu raportów możesz sprawdzić ich dostarczenie, sprawdzając otrzymane raporty oraz odpowiednie dzienniki serwerów, takie jak przykładowa historia serwera lub Twój system niestandardowy.

Dekodowanie raportu zbiorczego

W otrzymanym raporcie zbiorczym pole debug_cleartext_payload zawiera niezaszyfrowaną wersję tego raportu. Ta wersja raportu nie jest zaszyfrowana, ale nadal musi zostać zdekodowana.

Poniżej znajdziesz przykład dekodowania zawartości pola debug_cleartext_payload w 2 krokach: pierwszy z wykorzystaniem dekodowania Base64, a drugi z użyciem dekodowania 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

Jeśli chcesz zacząć korzystać z interfejsu Attribution Reporting API, możesz skorzystać z projektu MeasurementSampleApp na GitHubie. Ta przykładowa aplikacja prezentuje rejestrację źródła atrybucji i aktywatora.

W przypadku punktów końcowych serwera możesz skorzystać z tych zasobów referencyjnych lub własnego rozwiązania:

  • MeasurementAdTechServerSpec zawiera definicje usług OpenAPI, które możesz wdrożyć na obsługiwanych platformach próbnych lub mikroserwisów.
  • Parametr MeasurementAdTechServer zawiera referencyjną implementację przykładowego serwera opartego na aplikacji Spring Boot dla Google App Engine.

Wymagania wstępne

Wdrażaj przykładowe interfejsy API w zdalnych punktach końcowych dostępnych z urządzenia testowego lub emulatora. Dla ułatwienia testowania zapoznaj się z przykładowymi projektami MeasurementAdTechServerSpec i MeasurementAdTechServer.

Testowane funkcje

Nadchodzące funkcje

Elastyczna konfiguracja na poziomie zdarzenia

Domyślna konfiguracja raportowania na poziomie zdarzenia jest zalecana na potrzeby testowania narzędzi, ale może nie być idealna w niektórych przypadkach. Interfejs Attribution Reporting API będzie obsługiwać opcjonalne, bardziej elastyczne konfiguracje, aby technologie reklamowe miały większą kontrolę nad strukturą raportów na poziomie zdarzenia i mogły maksymalizować przydatność danych. Tę dodatkową elastyczność będziemy wprowadzać w interfejsie Attribution Reporting API w dwóch etapach:

  • Faza 1. Elastyczna konfiguracja na poziomie zdarzenia w wersji uproszczonej; podzbiór fazy 2.
  • Faza 2. Pełna wersja elastycznej konfiguracji na poziomie zdarzenia.

Faza 1. Elastyczny poziom zdarzenia w wersji uproszczonej

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

  • 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 jest przeznaczona dla dewelopera, który chce zoptymalizować witrynę pod kątem otrzymywania raportów we 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
  }
}

Faza 2. Pełny elastyczny poziom zdarzenia

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

{
  // 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 z uwzględnieniem poszczególnych rejestracji źródeł. W przypadku każdej specyfikacji aktywatora w pełni określamy:

  • Zestaw kryteriów spełniających kryteria:
    • Dane aktywatora, do których ma zastosowanie ta specyfikacja. To źródło może być dopasowywane tylko w przypadku reguł, które mają jedną z określonych wartości trigger_data w elemencie trigger_specs. Inaczej mówiąc, jeśli reguła spowodowałaby dopasowanie do tego źródła, ale jego trigger_data nie jest jedną z wartości w konfiguracji źródła, jest ona ignorowana.
    • Gdy określona reguła jest zgodna z tą specyfikacją (za pomocą funkcji event_report_windows). Pamiętaj, że w przypadku raportów zbiorczych może ona zostać dopasowana do źródła w przypadku raportów zbiorczych, mimo że nie zostaną spełnione 2 kryteria dopasowania wymienione wcześniej.
  • Specjalny algorytm podsumowujący i grupujący wszystkie reguły w oknie atrybucji. Dzięki temu aktywatory mogą określać parametr value, który jest zsumowany dla określonej specyfikacji, ale raportowany jako wartość zgrupowana.

Aktywatory będą też obsługiwać dodawanie opcjonalnego parametru wartości w słownikach w obrębie event_trigger_data.

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

Każda rejestracja aktywatora będzie pasować do maksymalnie 1 specyfikacji aktywatora i zaktualizuje powiązaną z nią wartość podsumowania. Ogólnie w momencie aktywowania:

  • Zastosuj globalne filtry atrybucji.
  • W przypadku każdej specyfikacji aktywatora przeanalizuj event_trigger_data w specyfikacji, aby znaleźć dopasowanie, korzystając z parametru event_reporting_window specyfikacji. Najwyższego poziomu event_reporting_windows jest wartością domyślną na wypadek, gdy specyfikacja aktywatora nie zawiera pola podrzędnego event_report_windows.
  • Do atrybucji wybierana jest pierwsza pasująca specyfikacja, a wartość podsumowania jest zwiększana o value.

Po ukończeniu właściwości event_report_window określonej specyfikacji zmapujemy jej wartość podsumowania na zasobnik i wyślemy raport na poziomie zdarzenia dla każdego przyrostu wartości w zasobniku podsumowania spowodowanego przez przypisane wartości aktywatorów. Raporty będą zawierały jedno dodatkowe pole: trigger_summary_bucket.

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

Konfiguracje równoważne z bieżącą wersją

Poniżej znajdziesz równoważne konfiguracje odpowiednio dla bieżącego zdarzenia i źródeł nawigacji w interfejsach API. Szczególnie w przypadku źródeł nawigacyjnych pokazuje to, dlaczego poziom szumu jest tak wysoki w porównaniu ze źródłami zdarzeń, aby utrzymać te same wartości w postaci epsilonów: źródła nawigacyjne mają znacznie większą przestrzeń wyjściową.

Istnieje wiele równoważnych konfiguracji, ponieważ niektóre parametry można ustawić jako domyślne lub przycięte.

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ównoważne źródła nawigacji
// 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, które wykraczają poza domyślne. We wszystkich tych przykładach kompromisy związane z deweloperem obejmują:

  • redukcję pewnego wymiaru konfiguracji domyślnej (#reguły, moc zbioru danych aktywatora, #okna), aby zwiększyć dodatkowy wymiar w celu zachowania poziomu szumu
  • redukcję niektórych wymiarów domyślnej konfiguracji (#reguł, mocy zbioru danych wyzwalających, #okna) w celu zmniejszenia poziomu szumu

Zbiorczych wartości aktywatorów raportów

Ta przykładowa konfiguracja służy deweloperowi, który chce zoptymalizować dane o wartości tylko w jednym okresie raportowania (np. 7 dni), a przez to ograniczyć liczbę okien do raportowania, aby zmniejszyć ilość szumu. W tym przykładzie każda reguła ustawiająca dla parametru trigger_data wartość inną niż 0 nie kwalifikuje się do atrybucji.

{
  "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]
  }],
}

Aktywatory można zarejestrować za pomocą zbioru pól value, który jest sumowany i grupowany. Jeśli np. w ciągu 7 dni od zarejestrowania w źródle występują 3 reguły 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 następujących raportach po 7 dniach i 1 godzinie:

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

W ciągu kolejnych 7 dni rejestrowane są te aktywatory:

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

Wartości są sumowane do 8 + 50 + 45 = 103. Po 14 dniach i po 1 godzinie generowane są te raporty:

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

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

Ten przykład pokazuje, jak deweloper może skonfigurować źródło, aby uzyskać liczbę reguł 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 reguły, których atrybut trigger_data ma wartość 0, są zliczane i ograniczane do 10. Wartość aktywatora jest ignorowana, ponieważ zasada summary_window_operator jest ustawiona na zliczanie. Jeśli zostaną zarejestrowane 4 reguły 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]
}
Plik binarny z częstszym raportowaniem

Ta przykładowa konfiguracja jest przeznaczona dla dewelopera, który chce się dowiedzieć, czy w ciągu pierwszych 10 dni wystąpiła co najmniej 1 konwersja (niezależnie od wartości), ale chce otrzymywać raporty częściej niż domyślna. W tym przykładzie każda reguła ustawiająca dla parametru trigger_data wartość inną niż 0 nie kwalifikuje się do atrybucji. Dlatego właśnie ten przypadek użycia jest określany jako binarny.

{
  "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]
  }],
}
Różne specyfikacje aktywatorów z różnych źródeł
{
  "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 zgłaszania innych przypadków użycia tego rozszerzenia interfejsu API – w razie potrzeby zaktualizujemy to wyjaśnienie o przykładowe konfiguracje.

Atrybucja międzysieciowa bez przekierowań

Specjaliści ds. technologii reklamowych powinni używać przekierowań, aby rejestrować wiele reguł powiązanych ze źródłami atrybucji i przeprowadzać atrybucję międzysieciową. Ta funkcja pomaga obsługiwać atrybucję międzysieciową, w których przekierowania między sieciami są niemożliwe. Więcej informacji

Technologie reklamowe mogą wysyłać konfigurację w odpowiedzi na temat rejestracji reguły na podstawie tego, które źródła zarejestrowane przez inne technologie reklamowe zostały wybrane do wygenerowania źródeł derywowanych. Te źródła są następnie używane do atrybucji. Raporty zbiorcze są generowane, jeśli reguła zostanie przypisana do źródła pochodnego. Generowanie raportu o zdarzeniach nie jest obsługiwane w przypadku źródeł derywowanych.

Technologie reklamowe mogą wybierać spośród zarejestrowanych źródeł w tych zasobach: aggregation_keys i udostępniać je partnerom technologii reklamowych. Te klucze można zadeklarować w opcjonalnym polu shared_aggregation_keys znajdującym się pod nagłówkiem rejestracji źródłowej Attribution-Reporting-Register-Source:

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

Źródła derywowane są generowane na podstawie konfiguracji w nagłówku rejestracji aktywatora 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"
    }
  ]

Dodaliśmy 2 nowe pola opcjonalne, które aktywują nagłówek rejestracji. Te pola umożliwiają podanie identyfikatora zwycięskiej technologii reklamowej w kluczach raportów, które można połączyć:

  • x_network_bit_mapping: identyfikator rejestracji na mapowanie bitów identyfikatora technologii reklamowych
  • x_network_data: przesunięcie w lewo (w lewo) w przypadku zwycięskiej technologii reklamowej x_network_bit_mapping LUB przy użyciu elementu klucza aktywującego
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
  }
  …
}

Tak wygląda obliczenie klucza reguły podczas generowania raportu dla źródła AdTechB:

  • key_piece: 0x400 (010000000000)
  • key_offset: 12
  • Wartość parametru enrollment_id AdtechB: 2 (010) (od x_network_bit_mapping)
  • Wynikowy klucz aktywatora: 0x400 | 0x2 << 12 = 0x2400

Ograniczenia

Listę trwających funkcji środowiska wykonawczego SDK znajdziesz w informacjach o wersji.

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

Twoja opinia jest dla nas bardzo ważnym elementem Piaskownicy prywatności na Androida. Daj nam znać o wszelkich problemach lub pomysłach na ulepszenie Piaskownicy prywatności na Androidzie.