Hướng dẫn dành cho nhà phát triển về API Báo cáo phân bổ

Khi bạn đọc tài liệu Hộp cát về quyền riêng tư trên Android, hãy sử dụng nút Bản dùng thử cho nhà phát triển hoặc Beta để chọn phiên bản chương trình bạn đang làm việc, vì hướng dẫn có thể khác nhau.


API Báo cáo phân bổ được thiết kế nhằm tăng cường quyền riêng tư của người dùng, thông qua việc loại bỏ sự phụ thuộc vào giá trị nhận dạng người dùng giữa nhiều bên, đồng thời hỗ trợ các trường hợp sử dụng chính để phân bổ và đo lường lượt chuyển đổi trên các ứng dụng. Hướng dẫn cho nhà phát triển này mô tả cách định cấu hình và kiểm thử các API Báo cáo phân bổ để đăng ký lượt nhấp, lượt xem và lượt chuyển đổi quảng cáo bằng các phương thức gọi giúp đăng ký điều kiện kích hoạt và nguồn có liên quan cho các sự kiện đó.

Hướng dẫn này sẽ chỉ cho bạn cách thiết lập điểm cuối của máy chủ và xây dựng một ứng dụng khách để gọi các dịch vụ này. Tìm hiểu thêm về thiết kế tổng thể của API Báo cáo phân bổ trong phần đề xuất thiết kế.

Từ khoá

  • Nguồn phân bổ đề cập đến số lượt nhấp hoặc số lượt xem.
  • Trình kích hoạt là các sự kiện có thể phân bổ cho các lượt chuyển đổi.
  • Báo cáo chứa dữ liệu về một trình kích hoạt và nguồn phân bổ tương ứng. Các báo cáo này được gửi để phản hồi lại các sự kiện kích hoạt. API Báo cáo phân bổ hỗ trợ các báo cáo cấp sự kiệnbáo cáo tổng hợp.

Trước khi bắt đầu

Để sử dụng API Báo cáo phân bổ, hãy hoàn thành các tác vụ phía máy chủ và phía máy khách được liệt kê trong những phần sau đây.

Thiết lập điểm cuối của API Báo cáo phân bổ

API Báo cáo phân bổ yêu cầu một tập hợp các điểm cuối mà bạn có thể truy cập từ một thiết bị kiểm thử hoặc trình mô phỏng. Tạo một điểm cuối cho mỗi thao tác phía máy chủ sau đây:

Có một số phương pháp để thiết lập các điểm cuối bắt buộc:

  • Cách nhanh nhất để thiết lập và chạy đó là triển khai các định nghĩa dịch vụ OpenAPI phiên bản 3 trong kho lưu trữ mã mẫu của chúng tôi cho nền tảng mô phỏng hoặc nền tảng vi dịch vụ. Bạn có thể sử dụng Postman, Prism hoặc bất kỳ nền tảng máy chủ mô phỏng nào khác chấp nhận định dạng này. Triển khai từng điểm cuối và theo dõi các URI để dùng trong ứng dụng của bạn. Để xác minh việc phân phối báo cáo, hãy tham khảo các lệnh gọi đã thực hiện trước đó đến nền tảng mô phỏng hoặc nền tảng không máy chủ.
  • Chạy máy chủ độc lập của bạn bằng mẫu Kotlin dựa trên Spring Boot. Triển khai máy chủ này trên nhà cung cấp dịch vụ đám mây hoặc cơ sở hạ tầng có trong máy của bạn.
  • Sử dụng định nghĩa dịch vụ làm ví dụ để tích hợp điểm cuối vào hệ thống hiện tại của bạn.

Chấp nhận đăng ký nguồn

Có thể xác định điểm cuối này từ một URI tương tự như sau:

https://adtech.example/attribution_source

Khi ứng dụng khách đăng ký một nguồn phân bổ, ứng dụng đó sẽ cung cấp URI cho điểm cuối của máy chủ này. Sau đó, API Báo cáo phân bổ sẽ gửi yêu cầu và dùng một trong các tiêu đề sau đây:

  • Đối với sự kiện nhấp chuột:

    Attribution-Reporting-Source-Info: navigation
    
  • Đối với sự kiện xem:

    Attribution-Reporting-Source-Info: event
    

Định cấu hình điểm cuối của máy chủ để phản hồi những nội dung sau:

// 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>

Dưới đây là một ví dụ về các giá trị mẫu được thêm vào:

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

Nếu Attribution-Reporting-Redirects chứa URI của các đối tác công nghệ quảng cáo, thì API báo cáo phân bổ sẽ thực hiện một yêu cầu tương tự cho từng URI. Mỗi đối tác công nghệ quảng cáo phải định cấu hình một máy chủ phản hồi bằng các tiêu đề sau:

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.

Chấp nhận đăng ký trình kích hoạt chuyển đổi

Có thể xác định điểm cuối này từ một URI tương tự như sau:

https://adtech.example/attribution_trigger

Khi một ứng dụng khách đăng ký một sự kiện kích hoạt, ứng dụng đó sẽ cung cấp URI cho điểm cuối của máy chủ này. Sau đó, API Báo cáo phân bổ sẽ gửi yêu cầu và dùng một trong các tiêu đề sau đây:

Định cấu hình điểm cuối của máy chủ để phản hồi những nội dung sau:

// 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>

Dưới đây là một ví dụ về các giá trị mẫu được thêm vào:

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

Tồn tại giới hạn 25 byte cho mỗi mã nhận dạng khoá tổng hợp và chuỗi bộ lọc. Điều này có nghĩa là mã nhận dạng khoá tổng hợp và chuỗi bộ lọc không được vượt quá 25 ký tự. Trong ví dụ này, campaignCounts có 14 ký tự nên đây là một mã nhận dạng khoá tổng hợp hợp lệ và 1234 có 4 ký tự nên đây là một chuỗi bộ lọc hợp lệ. Nếu mã nhận dạng khoá tổng hợp hoặc chuỗi bộ lọc vượt quá 25 ký tự, thì điều kiện kích hoạt sẽ bị bỏ qua.

Nếu Attribution-Reporting-Redirect chứa URI của các đối tác công nghệ quảng cáo, thì API Báo cáo phân bổ sẽ gửi một yêu cầu tương tự đến từng URI. Mỗi đối tác công nghệ quảng cáo phải định cấu hình một máy chủ phản hồi bằng các tiêu đề sau:

// 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.

Chấp nhận báo cáo ở cấp sự kiện

Phải xác định được điểm cuối này từ URI. Hãy xem bài viết Đăng ký tài khoản Hộp cát về quyền riêng tư để biết thêm thông tin về cách đăng ký URI. (URI được suy ra từ nguồn gốc của máy chủ được dùng để chấp nhận hoạt động đăng ký nguồn và đăng ký điều kiện kích hoạt). Sử dụng URI mẫu cho các điểm cuối chấp nhận hoạt động đăng ký nguồnchấp nhận hoạt động đăng ký điều kiện kích hoạt, URI của điểm cuối này là:

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

Định cấu hình máy chủ này để chấp nhận các yêu cầu JSON sử dụng định dạng sau:

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

Với khoá gỡ lỗi, bạn có thể xem thêm thông tin chi tiết về các báo cáo phân bổ; hãy tìm hiểu thêm về cách định cấu hình những khoá này.

Chấp nhận báo cáo tổng hợp

Phải xác định được điểm cuối này từ URI. Hãy xem bài viết Đăng ký tài khoản Hộp cát về quyền riêng tư để biết thêm thông tin về cách đăng ký URI. (URI được suy ra từ nguồn gốc của máy chủ được dùng để chấp nhận hoạt động đăng ký nguồn và đăng ký điều kiện kích hoạt). Sử dụng URI mẫu cho các điểm cuối chấp nhận hoạt động đăng ký nguồnchấp nhận hoạt động đăng ký điều kiện kích hoạt, URI của điểm cuối này là:

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

Cả những trường đã mã hoá và chưa mã hoá đều được điền sẵn dữ liệu cho các báo cáo tổng hợp. Với báo cáo đã mã hoá, bạn có thể bắt đầu kiểm thử bằng dịch vụ tổng hợp, còn trường chưa mã hoá cung cấp thông tin chi tiết về cách các cặp giá trị/khoá đã đặt đang xây dựng cấu trúc dữ liệu.

Định cấu hình máy chủ này để chấp nhận các yêu cầu JSON sử dụng định dạng sau:

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

Với khoá gỡ lỗi, bạn có thể xem thêm thông tin chi tiết về các báo cáo phân bổ; hãy tìm hiểu thêm về cách định cấu hình những khoá này.

Thiết lập ứng dụng khách Android

Ứng dụng khách này đăng ký các nguồn và điều kiện kích hoạt phân bổ, đồng thời giúp tạo báo cáo cấp sự kiện và báo cáo tổng hợp. Để chuẩn bị một thiết bị khách hoặc trình mô phỏng cho ứng dụng Android nhằm sử dụng API Báo cáo phân bổ, hãy làm như sau:

  1. Thiết lập môi trường phát triển cho Hộp cát về quyền riêng tư trên Android.
  2. Cài đặt hình ảnh hệ thống trên một thiết bị được hỗ trợ hoặc thiết lập một trình mô phỏng có hỗ trợ Hộp cát về quyền riêng tư trên Android.
  3. Cấp quyền truy cập vào API Báo cáo phân bổ bằng cách chạy lệnh ADB sau đây. (Theo mặc định, API bị tắt.)

    adb shell device_config put adservices ppapi_app_allow_list \"\*\"
  4. Nếu bạn đang kiểm thử cục bộ Attribution Reporting API (chẳng hạn như kiểm thử trên một thiết bị mà bạn có quyền truy cập thực tế), hãy chạy lệnh sau để tắt tuỳ chọn đăng ký:

    adb shell device_config put adservices disable_measurement_enrollment_check "true"
  5. Đưa quyền ACCESS_ADSERVICES_ATTRIBUTION vào tệp kê khai Android và tạo một cấu hình dịch vụ quảng cáo để ứng dụng của bạn sử dụng Attribution Reporting API:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
    
  6. (Không bắt buộc) Nếu bạn định nhận báo cáo gỡ lỗi, hãy thêm quyền ACCESS_ADSERVICES_AD_ID vào tệp kê khai Android:

    <uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" />
    
  7. Tham chiếu một cấu hình dịch vụ quảng cáo trong phần tử <application> của tệp kê khai:

    <property android:name="android.adservices.AD_SERVICES_CONFIG"
              android:resource="@xml/ad_services_config" />
    
  8. Chỉ định tài nguyên XML của dịch vụ quảng cáo được tham chiếu trong tệp kê khai, chẳng hạn như res/xml/ad_services_config.xml. Hãy tìm hiểu thêm về việc kiểm soát quyền truy cập vào SDK và dịch vụ quảng cáo.

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

Đăng ký sự kiện quảng cáo

Ứng dụng của bạn phải đăng ký các nguồn và lượt chuyển đổi khi những nguồn và lượt chuyển đổi này xuất hiện để đảm bảo báo cáo chính xác. Lớp MeasurementManager có các phương thức giúp bạn đăng ký sự kiện nguồn phân bổđiều kiện kích hoạt chuyển đổi.

Đăng ký một sự kiện nguồn phân bổ

Khi người dùng xem hoặc nhấp vào quảng cáo, ứng dụng của nhà phát hành sẽ gọi registerSource() để đăng ký nguồn phân bổ, như minh hoạ trong đoạn mã.

API Báo cáo phân bổ hỗ trợ các loại sự kiện nguồn phân bổ sau đây:

  • Lượt nhấp mà bạn thường đăng ký trong phương pháp gọi lại tương tự như onClick(). Sự kiện kích hoạt tương ứng thường xảy ra ngay sau sự kiện nhấp chuột. Loại sự kiện này cung cấp thêm thông tin về hoạt động tương tác của người dùng, do đó, đây là loại nguồn phân bổ tốt để có mức độ ưu tiên cao.
  • Lượt xem mà bạn thường đăng ký trong phương pháp gọi lại tương tự như onAdShown(). Sự kiện kích hoạt tương ứng có thể xảy ra sau vài giờ hoặc vài ngày kể từ sự kiện xem.

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);

Sau khi đăng ký, API sẽ gửi một yêu cầu POST qua HTTP tới điểm cuối của dịch vụ tại địa chỉ do attributionSourceUri chỉ định. Phản hồi của điểm cuối bao gồm các giá trị cho destination, source_event_id, expirysource_priority.

Nếu công nghệ quảng cáo ban đầu muốn chia sẻ các lượt đăng ký nguồn, thì URI nguồn phân bổ ban đầu có thể bao gồm các lệnh chuyển hướng đến điểm cuối khác của công nghệ quảng cáo. Giới hạn và quy tắc áp dụng cho các lệnh chuyển hướng sẽ được nêu chi tiết trong đề xuất kỹ thuật.

Chúng tôi đã thêm tính năng hỗ trợ cho các lần chuyển hướng chuỗi kết nối cho registerSourceregisterTrigger. Ngoài tiêu đề đăng ký, giờ đây, người dùng API có thể cung cấp lệnh chuyển hướng HTTP dưới dạng phản hồi của máy chủ. Lệnh chuyển hướng này bao gồm mã trạng thái 302 và tiêu đề "Vị trí" có URL tiếp theo cần truy cập để đăng ký bổ sung.

Hiện tại, chỉ có trường "destination" (đích đến) đã cung cấp trong lượt truy cập đầu tiên là được dùng trên chuỗi kết nối. Số lượt truy cập có cùng giới hạn với tiêu đề "Attribution-Reporting-Redirect" (Chuyển hướng báo cáo phân bổ). Tính năng hỗ trợ chuyển hướng này bổ sung cho tính năng hỗ trợ "Attribution-Reporting-Redirect" hiện có và nếu có cả hai, tính năng "Attribution-Reporting-Redirect" sẽ được ưu tiên.

Đăng ký một sự kiện cho điều kiện kích hoạt lượt chuyển đổi

Để đăng ký một sự kiện kích hoạt lượt chuyển đổi, hãy gọi registerTrigger() trong ứng dụng của bạn:

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)

Sau khi đăng ký, API sẽ gửi một yêu cầu POST HTTP tới điểm cuối của dịch vụ tại địa chỉ do attributionTriggerUri chỉ định. Phản hồi của điểm cuối bao gồm các giá trị cho báo cáo sự kiện và báo cáo tổng hợp.

Nếu nền tảng công nghệ quảng cáo ban đầu cho phép chia sẻ lượt đăng ký điều kiện kích hoạt, URI có thể bao gồm các lệnh chuyển hướng đến URI thuộc nền tảng khác của công nghệ quảng cáo. Giới hạn và quy tắc áp dụng cho các lệnh chuyển hướng được nêu chi tiết trong đề xuất kỹ thuật.

Đăng ký hoạt động đo lường trên nhiều ứng dụng và web

Trong trường hợp cả ứng dụng và trình duyệt đều đóng vai trò trong hành trình của người dùng từ nguồn đến điều kiện kích hoạt, sẽ có những khác biệt nhỏ khi triển khai hoạt động đăng ký sự kiện quảng cáo. Nếu người dùng nhìn thấy một quảng cáo trên ứng dụng và được chuyển hướng đến trình duyệt để chuyển đổi, thì nguồn sẽ được ứng dụng đăng ký và lượt chuyển đổi sẽ được trình duyệt web đăng ký. Tương tự, nếu người dùng khởi động trên trình duyệt web và được chuyển đến một ứng dụng để chuyển đổi, thì trình duyệt sẽ đăng ký nguồn, còn ứng dụng đó sẽ đăng ký lượt chuyển đổi.

Vì có sự khác biệt về cách sắp xếp công nghệ quảng cáo trên web và trên Android, chúng tôi đã thêm các API mới để đăng ký nguồn và điều kiện kích hoạt khi chúng diễn ra trên trình duyệt. Điểm khác biệt chính giữa các API này và các API dựa trên ứng dụng tương ứng là chúng tôi mong muốn trình duyệt tuân theo các lệnh chuyển hướng, áp dụng bất kỳ bộ lọc nào dành riêng cho trình duyệt và truyền các lượt đăng ký hợp lệ cho nền tảng bằng cách gọi registerWebSource() hoặc registerWebTrigger().

Đoạn mã sau đây cho thấy ví dụ về lệnh gọi API mà trình duyệt thực hiện để đăng ký nguồn phân bổ trước khi chuyển người dùng đến ứng dụng:

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);

Đoạn mã sau đây cho thấy ví dụ về lệnh gọi API mà trình duyệt thực hiện để đăng ký lượt chuyển đổi sau khi người dùng được chuyển từ ứng dụng:

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);

Thêm âm thanh để đảm bảo quyền riêng tư

Báo cáo ở cấp sự kiện chứa dữ liệu về đích, giá trị nhận dạng nguồn phân bổ và điều kiện kích hoạt. Những báo cáo này được gửi ở định dạng gốc (chưa mã hoá) đến nguồn gốc báo cáo. Để bảo vệ quyền riêng tư của người dùng, bạn có thể thêm âm thanh để khó xác định một người dùng hơn. Báo cáo có âm thanh ở cấp sự kiện sẽ được tạo và gửi theo khung sự riêng tư biệt lập. Dưới đây là các giá trị phần trăm mặc định về âm thanh cho nhiều tình huống:

Loại nguồn

Giá trị đích đến của nguồn

Xác suất báo cáo có âm thanh trên mỗi lượt đăng ký nguồn

Xem

Ứng dụng hoặc web

0,0000025

Xem

Ứng dụng và web

0,0000042

Nhấp

Ứng dụng hoặc web

0,0024263

Nhấp

Ứng dụng và web

0,0170218

Trong mô hình đo lường phân bổ từ ứng dụng đến web, nơi các nguồn có thể thúc đẩy lượt chuyển đổi đến cả đích đến ứng dụng và web, các báo cáo ở cấp sự kiện có thể chỉ định xem điều kiện kích hoạt xuất hiện trên ứng dụng hay trên web. Để bù đắp cho chi tiết bổ sung này, các báo cáo có âm thanh được tạo ra sẽ tăng xấp xỉ 7 lần số lượt nhấp và xấp xỉ 1,7 lần số lượt xem.

Một số công nghệ quảng cáo không yêu cầu báo cáo ở cấp sự kiện chỉ định xem điều kiện kích hoạt xuất hiện trên đích đến ứng dụng hay đích đến web. Công nghệ quảng cáo có thể sử dụng trường coarse_event_report_destinations trong tiêu đề Attribution-Reporting-Register-Source để giảm bớt âm thanh. Nếu một nguồn được chỉ định trường coarse_event_report_destinations giành được lượt phân bổ, báo cáo thu được sẽ bao gồm cả đích đến ứng dụng và đích đến web, mà không có sự phân biệt về vị trí thực sự xuất hiện điều kiện kích hoạt.

Trong các ví dụ sau đây, người dùng nhấp vào một quảng cáo và nguồn đó sẽ được đăng ký bằng API. Sau đó, người dùng chuyển đổi trên cả ứng dụng và trang web của nhà quảng cáo. Cả hai lượt chuyển đổi này đều được đăng ký là điều kiện kích hoạt và được phân bổ cho lượt nhấp ban đầu.

Tiêu đề HTTP đăng ký nguồn dựa trên lượt nhấp:

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"
}

Một điều kiện kích hoạt được đăng ký từ ứng dụng với tên gói com.advertiser.example:

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

Một điều kiện kích hoạt được đăng ký từ trình duyệt của trang web có miền eTLD+1 https://advertiser.com:

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

Các báo cáo ở cấp sự kiện thu được sẽ được tạo. Giả sử cả hai điều kiện kích hoạt đều được phân bổ cho nguồn, thì các báo cáo ở cấp sự kiện sau đây sẽ được tạo:

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

Tạo và phân phối báo cáo

API Báo cáo phân bổ gửi báo cáo đến các điểm cuối trên máy chủ của bạn để chấp nhận báo cáo cấp sự kiệnbáo cáo tổng hợp.

Buộc báo cáo các công việc cần chạy

Sau khi bạn đăng ký một sự kiện nguồn phân bổ hoặc đăng ký một sự kiện kích hoạt, hệ thống sẽ lên lịch chạy công việc báo cáo. Theo mặc định, công việc này sẽ chạy 4 giờ một lần. Đối với mục đích kiểm thử, bạn có thể buộc các công việc báo cáo chạy hoặc rút ngắn khoảng thời gian giữa các công việc.

Buộc thực hiện công việc phân bổ:

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

Buộc chạy công việc báo cáo ở cấp sự kiện:

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

Buộc thực hiện công việc báo cáo tổng hợp:

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

Kiểm tra đầu ra trong logcat để xem thời điểm các công việc đã thực hiện. Quy trình này sẽ diễn ra như sau:

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

Buộc phân phối các báo cáo

Ngay cả khi công việc báo cáo buộc phải chạy, hệ thống vẫn gửi báo cáo theo thời gian phân phối theo lịch, khoảng từ vài giờ đến vài ngày. Đối với mục đích kiểm thử, bạn có thể đặt trước thời gian hệ thống của thiết bị sau độ trễ đã lên lịch để bắt đầu gửi báo cáo.

Xác minh các báo cáo trên máy chủ của bạn

Sau khi báo cáo được gửi, hãy xác minh việc phân phối bằng cách kiểm tra các báo cáo đã nhận, nhật ký máy chủ có liên quan (chẳng hạn như nhật ký của máy chủ mô phỏng) hoặc hệ thống tuỳ chỉnh của bạn.

Giải mã báo cáo tổng hợp

Khi nhận được một báo cáo tổng hợp, trường debug_cleartext_payload sẽ lưu giữ phiên bản không mã hoá của báo cáo tổng hợp đó. Mặc dù phiên bản báo cáo này không được mã hoá, nhưng bạn vẫn cần giải mã phiên bản đó.

Dưới đây là ví dụ về cách giải mã nội dung của trường debug_cleartext_payload theo hai bước: bước đầu tiên sử dụng phương thức giải mã Base 64 và bước thứ hai sử dụng phương thức giải mã 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);
});

Kiểm thử

Để bắt đầu dùng API Báo cáo phân bổ, bạn có thể sử dụng dự án MeasurementSampleApp trên GitHub. Ứng dụng mẫu này minh hoạ việc đăng ký nguồn phân bổ và đăng ký điều kiện kích hoạt.

Đối với điểm cuối của máy chủ, hãy xem xét các tài nguyên tham chiếu sau đây hoặc giải pháp tuỳ chỉnh của bạn:

  • MeasurementAdTechServerSpec cung cấp các định nghĩa dịch vụ OpenAPI, có thể triển khai cho các nền tảng mô phỏng hoặc nền tảng dịch vụ vi mô được hỗ trợ.
  • MeasurementAdTechServer cung cấp cách triển khai tham chiếu đối với một máy chủ mô phỏng dựa trên ứng dụng Spring Boot dành cho Google App Engine.

Điều kiện tiên quyết

Triển khai API mô phỏng trên các điểm cuối từ xa có thể truy cập được từ thiết bị kiểm thử hoặc trình mô phỏng. Để dễ dàng kiểm thử, hãy tham khảo các dự án mẫu MeasurementAdTechServerSpecMeasurementAdTechServer.

Hàm để thử nghiệm

  • Thực hiện đăng ký nguồn phân bổ và điều kiện kích hoạt chuyển đổi. Kiểm tra để đảm bảo điểm cuối của phía máy chủ phản hồi bằng định dạng chính xác.
  • Thực thi công việc báo cáo.
  • Xác minh việc phân phối báo cáo trên bảng điều khiển hoặc chương trình phụ trợ của máy chủ kiểm thử.

Các tính năng sắp ra mắt

Cấu hình cấp sự kiện linh hoạt

Bạn nên sử dụng cấu hình mặc định cho báo cáo cấp sự kiện để bắt đầu kiểm thử tiện ích, nhưng điều này có thể không phù hợp cho mọi trường hợp sử dụng. Attribution Reporting API sẽ hỗ trợ các cấu hình không bắt buộc và linh hoạt hơn để công nghệ quảng cáo tăng cường kiểm soát cấu trúc của các báo cáo cấp sự kiện, đồng thời có thể tối đa hoá giá trị tiện ích của dữ liệu. Tính linh hoạt bổ sung này sẽ được đưa vào Attribution Reporting API theo hai giai đoạn:

  • Giai đoạn 1: Cấu hình cấp sự kiện linh hoạt và thu gọn; một tập con của Giai đoạn 2.
  • Giai đoạn 2: Phiên bản đầy đủ của cấu hình cấp sự kiện linh hoạt.

Giai đoạn 1: Cấp sự kiện linh hoạt và thu gọn

Chúng tôi sẽ thêm hai tham số không bắt buộc sau đây vào tệp JSON trong Attribution-Reporting-Register-Source:

  • 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>, ...]
  }
}

Ví dụ về cấu hình tuỳ chỉnh

Cấu hình mẫu này hỗ trợ nhà phát triển muốn tối ưu hoá để nhận được báo cáo trong các khoảng thời gian báo cáo được diễn ra sớm hơn.

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

Giai đoạn 2: Cấp sự kiện linh hoạt hoàn toàn

Ngoài các tham số đã thêm vào Giai đoạn 1, chúng tôi sẽ thêm một tham số không bắt buộc khác trigger_specs vào tệp JSON trong 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>, ...]
  }
}

Cấu hình này chỉ định đầy đủ không gian đầu ra của các báo cáo cấp sự kiện, theo mỗi lượt đăng ký nguồn. Đối với mọi thông số kỹ thuật của điều kiện kích hoạt, chúng tôi đều chỉ định đầy đủ:

  • Một nhóm tiêu chí phù hợp:
    • Dữ liệu cụ thể về điều kiện kích hoạt mà thông số kỹ thuật này áp dụng. Nguồn này chỉ đủ điều kiện để được so khớp với các điều kiện kích hoạt có một trong các giá trị trigger_data được chỉ định trong trigger_specs. Nói theo cách khác, nếu điều kiện kích hoạt khớp với nguồn này nhưng trigger_data của điều kiện kích hoạt này không phải là một trong các giá trị thuộc cấu hình của nguồn, thì điều kiện kích hoạt này sẽ bị bỏ qua.
    • Khi một điều kiện kích hoạt cụ thể khớp với thông số kỹ thuật này (bằng event_report_windows). Xin lưu ý rằng bạn vẫn có thể so khớp điều kiện kích hoạt qua một nguồn cho báo cáo tổng hợp, mặc dù điều kiện kích hoạt này không đáp ứng với 2 tiêu chí so khớp đã nêu ở phần trước đó.
  • Một thuật toán cụ thể để tóm tắt và phân giỏ toàn bộ điều kiện kích hoạt trong một thời lượng phân bổ. Điều này cho phép điều kiện kích hoạt chỉ định tham số value được tổng hợp cho một thông số kỹ thuật cụ thể, nhưng được báo cáo dưới dạng giá trị được phân giỏ.

Điều kiện kích hoạt cũng sẽ hỗ trợ việc thêm một tham số giá trị không bắt buộc vào các từ điển trong event_trigger_data.

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

Mỗi lượt đăng ký điều kiện kích hoạt sẽ so khớp với tối đa một thông số kỹ thuật của điều kiện kích hoạt này, đồng thời cập nhật giá trị tóm tắt có liên quan của lượt đăng ký điều kiện kích hoạt đó. Ở cấp độ cao, vào thời điểm kích hoạt, chúng tôi sẽ:

  • Áp dụng bộ lọc phân bổ chung.
  • Đối với mọi thông số kỹ thuật của điều kiện kích hoạt, đánh giá event_trigger_data trên thông số kỹ thuật để tìm kết quả trùng khớp, bằng cách sử dụng event_reporting_window của thông số kỹ thuật đó. Cấp cao nhất event_reporting_windows đóng vai trò là giá trị mặc định, trong trường hợp có thông số kỹ thuật của điều kiện kích hoạt là trường phụ event_report_windows bị thiếu.
  • Thông số kỹ thuật đã khớp đầu tiên được chọn cho quy trình phân bổ, và giá trị tóm tắt được tăng lên theo value.

Khi event_report_window cho một thông số kỹ thuật hoàn tất, chúng tôi sẽ ánh xạ giá trị tóm tắt của thông số đó với một nhóm, đồng thời gửi báo cáo cấp sự kiện cho mọi mức độ gia tăng trong nhóm tóm tắt được các giá trị của điều kiện kích hoạt được phân bổ tạo ra. Báo cáo sẽ có thêm một trường là trigger_summary_bucket.

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

Cấu hình tương đương với phiên bản hiện tại

Sau đây là các cấu hình tương đương cho các sự kiện hiện tại và nguồn điều hướng của API theo thứ tự tương ứng. Đặc biệt là đối với các nguồn điều hướng, điều này cho thấy tại sao mức độ nhiễu trở nên quá cao so với các nguồn sự kiện để duy trì cùng một giá trị epsilon – vì các nguồn điều hướng có không gian đầu ra lớn hơn nhiều.

Có thể sẽ có nhiều cấu hình tương đương với nhau, vì một số tham số có thể được đặt thành mặc định hoặc bị cắt bớt.

Các nguồn sự kiện tương đương
// 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>,
}
Các nguồn điều hướng tương đương
// 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>,
}

Ví dụ về cấu hình tuỳ chỉnh

Dưới đây là một số cấu hình bổ sung nằm ngoài cấu hình mặc định. Trong tất cả ví dụ này, nhà phát triển cũng cần lưu ý ưu nhược điểm của các cấu hình này, bao gồm:

  • giảm một số phương diện của cấu hình mặc định (chẳng hạn như #triggers, số lượng giá trị riêng biệt của dữ liệu về điều kiện kích hoạt, #windows) để tăng một phương diện khác nhằm duy trì độ nhiễu
  • giảm một số phương diện của cấu hình mặc định (chẳng hạn như #triggers, số lượng giá trị riêng biệt của dữ liệu về điều kiện kích hoạt, #windows) để giảm độ nhiễu

Báo cáo nhóm giá trị của điều kiện kích hoạt

Cấu hình mẫu này hỗ trợ nhà phát triển muốn tối ưu hoá dữ liệu về giá trị chỉ trong một khoảng thời gian báo cáo (ví dụ: 7 ngày), chấp nhận việc rút gọn khoảng thời gian báo cáo để giảm độ nhiễu. Trong ví dụ này, các điều kiện kích hoạt đặt trigger_data thành một giá trị khác 0 đều không đủ điều kiện để phân bổ.

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

Bạn có thể đăng ký điều kiện kích hoạt bằng nhóm trường value (được tổng hợp và phân giỏ). Ví dụ: nếu có 3 điều kiện kích hoạt trong vòng 7 ngày kể từ ngày đăng ký nguồn chứa giá trị 1, 3 và 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}] }

Các giá trị được cộng lại thành 8 rồi được báo cáo trong các báo cáo sau đây sau 7 ngày + 1 giờ:

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

Trong 7 ngày tiếp theo, các điều kiện kích hoạt sau đây sẽ được đăng ký:

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

Các giá trị được cộng lại thành 8+50+45 = 103. Thao tác này tạo các báo cáo sau đây trong 14 ngày + 1 giờ:

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

// Report 3
{
  ...
  "trigger_summary_bucket": [100, MAX_INT]
}
Báo cáo số lượng điều kiện kích hoạt

Ví dụ này cho thấy cách nhà phát triển có thể định cấu hình một nguồn để nhận được số lượng điều kiện kích hoạt đạt mức tối đa là 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]
  }],
}

Các điều kiện kích hoạt được phân bổ có trigger_data được đặt thành 0 sẽ được đếm và giới hạn ở mức 10. Giá trị của điều kiện kích hoạt bị bỏ qua vì summary_window_operator được đặt thành số lượng. Nếu 4 điều kiện kích hoạt được đăng ký và phân bổ cho nguồn, báo cáo sẽ có dạng như sau:

// 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]
}
Tệp nhị phân kèm với báo cáo được gửi thường xuyên hơn

Cấu hình mẫu này hỗ trợ một nhà phát triển muốn tìm hiểu xem liệu có ít nhất một lượt chuyển đổi xảy ra (bất kể giá trị) trong 10 ngày đầu tiên hay không, đồng thời muốn nhận được báo cáo ở mức độ thường xuyên hơn so với mặc định. Xin nhắc lại, trong ví dụ này, điều kiện kích hoạt đặt trigger_data thành một giá trị khác 0 đều không đủ điều kiện để phân bổ. Đó là lý do trường hợp sử dụng này được gọi là tệp nhị phân.

{
  "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]
  }],
}
Thay đổi thông số kỹ thuật của điều kiện kích hoạt từ nguồn này sang nguồn khác
{
  "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
}

Nhà phát triển nên đề xuất các trường hợp sử dụng mà họ có thể gặp phải cho tiện ích API này. Chúng tôi sẽ cập nhật bài giải thích này bằng cấu hình mẫu của những trường hợp sử dụng đó.

Phân bổ trên nhiều mạng mà không cần chuyển hướng

Công nghệ quảng cáo nên sử dụng lệnh chuyển hướng để đăng ký nhiều điều kiện kích hoạt nguồn phân bổ và để thực hiện hoạt động phân bổ trên nhiều mạng. Tính năng này giúp hỗ trợ hoạt động phân bổ trên nhiều mạng, trong đó việc chuyển hướng là không khả thi trên các mạng. Tìm hiểu thêm.

Công nghệ quảng cáo có thể gửi cấu hình trong phản hồi đăng ký điều kiện kích hoạt dựa trên những nguồn được đăng ký bởi các công nghệ quảng cáo khác được chọn để tạo nguồn phái sinh; sau đó, các nguồn phái sinh này sẽ được dùng cho hoạt động phân bổ. Hệ thống sẽ tạo báo cáo tổng hợp nếu điều kiện kích hoạt được phân bổ đến một nguồn phái sinh. Hệ thống không hỗ trợ việc tạo báo cáo sự kiện cho các nguồn phái sinh.

Các công nghệ quảng cáo có thể chọn trong số aggregation_keys trên các nguồn đã đăng ký mà họ dự định chia sẻ với công nghệ quảng cáo của đối tác. Bạn có thể khai báo các khoá này trong trường shared_aggregation_keys không bắt buộc, nằm trong tiêu đề đăng ký nguồn Attribution-Reporting-Register-Source:

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

Các nguồn phái sinh được tạo dựa trên cấu hình trong tiêu đề đăng ký điều kiện kích hoạt 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]"
    }
  ]

Dưới đây là phiên bản đã thêm giá trị mẫu:

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

2 trường mới (không bắt buộc) được thêm vào để kích hoạt tiêu đề đăng ký. Các trường này hỗ trợ giá trị nhận dạng của công nghệ quảng cáo chiến thắng trong các khoá của báo cáo tổng hợp:

  • x_network_bit_mapping: Mã đăng ký đến liên kết dạng bit của mã nhận dạng công nghệ quảng cáo
  • x_network_data: Mức chênh lệch (di chuyển sang trái) cho thao tác OR x_network_bit_mapping của công nghệ quảng cáo chiến thắng có phần khoá điều kiện kích hoạt
Ví dụ:
"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
  }
  
}

Dưới đây là kết quả tính toán phần khoá của điều kiện kích hoạt khi tạo báo cáo cho nguồn của AdTechB:

  • key_piece: 0x400 (010000000000)
  • key_offset: 12
  • Giá trị enrollment_id của AdtechB: 2 (010) (từ x_network_bit_mapping)
  • Phần khoá của điều kiện kích hoạt kết quả: 0x400 | 0x2 << 12 = 0x2400

Các điểm hạn chế

Để biết danh sách tính năng đang trong quá trình phát triển cho Thời gian chạy SDK, vui lòng xem ghi chú phát hành.

Báo cáo lỗi và vấn đề

Ý kiến phản hồi của bạn đóng vai trò quan trọng đối với Hộp cát về quyền riêng tư trên Android! Vui lòng cho chúng tôi biết bất kỳ vấn đề nào hoặc ý tưởng để cải thiện Hộp cát về quyền riêng tư trên Android.