1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adservices.service.measurement.reporting; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.net.Uri; 22 import android.util.Pair; 23 24 import androidx.annotation.Nullable; 25 26 import com.android.adservices.LogUtil; 27 import com.android.adservices.data.measurement.DatastoreException; 28 import com.android.adservices.data.measurement.DatastoreManager; 29 import com.android.adservices.data.measurement.DatastoreManagerFactory; 30 import com.android.adservices.data.measurement.IMeasurementDao; 31 import com.android.adservices.service.Flags; 32 import com.android.adservices.service.measurement.EventSurfaceType; 33 import com.android.adservices.service.measurement.Source; 34 import com.android.adservices.service.measurement.Trigger; 35 import com.android.adservices.service.measurement.noising.SourceNoiseHandler; 36 import com.android.adservices.service.measurement.util.UnsignedLong; 37 import com.android.adservices.service.measurement.util.Web; 38 39 import com.google.common.annotations.VisibleForTesting; 40 41 import org.json.JSONException; 42 import org.json.JSONObject; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 import java.util.Objects; 47 import java.util.Optional; 48 import java.util.UUID; 49 import java.util.concurrent.TimeUnit; 50 51 /** Class used to send debug reports to Ad-Tech {@link DebugReport} */ 52 public class DebugReportApi { 53 54 /** Define different verbose debug report types. */ 55 public interface Type { 56 String SOURCE_DESTINATION_LIMIT = "source-destination-limit"; 57 String SOURCE_NOISED = "source-noised"; 58 String SOURCE_STORAGE_LIMIT = "source-storage-limit"; 59 String SOURCE_SUCCESS = "source-success"; 60 String SOURCE_UNKNOWN_ERROR = "source-unknown-error"; 61 String TRIGGER_AGGREGATE_DEDUPLICATED = "trigger-aggregate-deduplicated"; 62 String TRIGGER_AGGREGATE_INSUFFICIENT_BUDGET = "trigger-aggregate-insufficient-budget"; 63 String TRIGGER_AGGREGATE_NO_CONTRIBUTIONS = "trigger-aggregate-no-contributions"; 64 String TRIGGER_AGGREGATE_REPORT_WINDOW_PASSED = "trigger-aggregate-report-window-passed"; 65 String TRIGGER_ATTRIBUTIONS_PER_SOURCE_DESTINATION_LIMIT = 66 "trigger-attributions-per-source-destination-limit"; 67 String TRIGGER_EVENT_DEDUPLICATED = "trigger-event-deduplicated"; 68 String TRIGGER_EVENT_EXCESSIVE_REPORTS = "trigger-event-excessive-reports"; 69 String TRIGGER_EVENT_LOW_PRIORITY = "trigger-event-low-priority"; 70 String TRIGGER_EVENT_NO_MATCHING_CONFIGURATIONS = 71 "trigger-event-no-matching-configurations"; 72 String TRIGGER_EVENT_NOISE = "trigger-event-noise"; 73 String TRIGGER_EVENT_REPORT_WINDOW_PASSED = "trigger-event-report-window-passed"; 74 String TRIGGER_NO_MATCHING_FILTER_DATA = "trigger-no-matching-filter-data"; 75 String TRIGGER_NO_MATCHING_SOURCE = "trigger-no-matching-source"; 76 String TRIGGER_REPORTING_ORIGIN_LIMIT = "trigger-reporting-origin-limit"; 77 String TRIGGER_EVENT_STORAGE_LIMIT = "trigger-event-storage-limit"; 78 String TRIGGER_UNKNOWN_ERROR = "trigger-unknown-error"; 79 String TRIGGER_AGGREGATE_STORAGE_LIMIT = "trigger-aggregate-storage-limit"; 80 } 81 82 /** Defines different verbose debug report body parameters. */ 83 @VisibleForTesting 84 public interface Body { 85 String ATTRIBUTION_DESTINATION = "attribution_destination"; 86 String LIMIT = "limit"; 87 String RANDOMIZED_TRIGGER_RATE = "randomized_trigger_rate"; 88 String SCHEDULED_REPORT_TIME = "scheduled_report_time"; 89 String SOURCE_DEBUG_KEY = "source_debug_key"; 90 String SOURCE_EVENT_ID = "source_event_id"; 91 String SOURCE_SITE = "source_site"; 92 String SOURCE_TYPE = "source_type"; 93 String TRIGGER_DATA = "trigger_data"; 94 String TRIGGER_DEBUG_KEY = "trigger_debug_key"; 95 } 96 97 private enum PermissionState { 98 GRANTED, 99 DENIED, 100 NONE 101 } 102 103 private final Context mContext; 104 private final Flags mFlags; 105 private final DatastoreManager mDatastoreManager; 106 private final EventReportWindowCalcDelegate mEventReportWindowCalcDelegate; 107 private final SourceNoiseHandler mSourceNoiseHandler; 108 DebugReportApi(Context context, Flags flags)109 public DebugReportApi(Context context, Flags flags) { 110 this( 111 context, 112 flags, 113 new EventReportWindowCalcDelegate(flags), 114 new SourceNoiseHandler(flags)); 115 } 116 117 @VisibleForTesting DebugReportApi( Context context, Flags flags, EventReportWindowCalcDelegate eventReportWindowCalcDelegate, SourceNoiseHandler sourceNoiseHandler)118 DebugReportApi( 119 Context context, 120 Flags flags, 121 EventReportWindowCalcDelegate eventReportWindowCalcDelegate, 122 SourceNoiseHandler sourceNoiseHandler) { 123 mContext = context; 124 mFlags = flags; 125 mDatastoreManager = DatastoreManagerFactory.getDatastoreManager(context); 126 mEventReportWindowCalcDelegate = eventReportWindowCalcDelegate; 127 mSourceNoiseHandler = sourceNoiseHandler; 128 } 129 130 /** Schedules the Source Success Debug Report */ scheduleSourceSuccessDebugReport(Source source, IMeasurementDao dao)131 public void scheduleSourceSuccessDebugReport(Source source, IMeasurementDao dao) { 132 if (isSourceDebugFlagDisabled(Type.SOURCE_SUCCESS)) { 133 return; 134 } 135 if (isAdTechNotOptIn(source.isDebugReporting(), Type.SOURCE_SUCCESS)) { 136 return; 137 } 138 if (getAdIdPermissionFromSource(source) == PermissionState.DENIED 139 || getArDebugPermissionFromSource(source) == PermissionState.DENIED) { 140 LogUtil.d("Skipping debug report %s", Type.SOURCE_SUCCESS); 141 return; 142 } 143 scheduleReport( 144 Type.SOURCE_SUCCESS, 145 generateSourceDebugReportBody(source, null), 146 source.getEnrollmentId(), 147 source.getRegistrationOrigin(), 148 dao); 149 } 150 151 /** Schedules the Source Destination limit Debug Report */ scheduleSourceDestinationLimitDebugReport( Source source, String limit, IMeasurementDao dao)152 public void scheduleSourceDestinationLimitDebugReport( 153 Source source, String limit, IMeasurementDao dao) { 154 if (isSourceDebugFlagDisabled(Type.SOURCE_DESTINATION_LIMIT)) { 155 return; 156 } 157 if (isAdTechNotOptIn(source.isDebugReporting(), Type.SOURCE_DESTINATION_LIMIT)) { 158 return; 159 } 160 try { 161 JSONObject body = new JSONObject(); 162 body.put(Body.SOURCE_EVENT_ID, source.getEventId().toString()); 163 body.put(Body.ATTRIBUTION_DESTINATION, generateSourceDestinations(source)); 164 body.put(Body.SOURCE_SITE, generateSourceSite(source)); 165 body.put(Body.LIMIT, limit); 166 if (getAdIdPermissionFromSource(source) == PermissionState.GRANTED 167 || getArDebugPermissionFromSource(source) == PermissionState.GRANTED) { 168 body.put(Body.SOURCE_DEBUG_KEY, source.getDebugKey()); 169 } 170 scheduleReport( 171 Type.SOURCE_DESTINATION_LIMIT, 172 body, 173 source.getEnrollmentId(), 174 source.getRegistrationOrigin(), 175 dao); 176 } catch (JSONException e) { 177 LogUtil.e(e, "Json error in debug report %s", Type.SOURCE_DESTINATION_LIMIT); 178 } 179 } 180 181 /** Schedules the Source Noised Debug Report */ scheduleSourceNoisedDebugReport(Source source, IMeasurementDao dao)182 public void scheduleSourceNoisedDebugReport(Source source, IMeasurementDao dao) { 183 if (isSourceDebugFlagDisabled(Type.SOURCE_NOISED)) { 184 return; 185 } 186 if (isAdTechNotOptIn(source.isDebugReporting(), Type.SOURCE_NOISED)) { 187 return; 188 } 189 if (getAdIdPermissionFromSource(source) == PermissionState.DENIED 190 || getArDebugPermissionFromSource(source) == PermissionState.DENIED) { 191 LogUtil.d("Skipping debug report %s", Type.SOURCE_NOISED); 192 return; 193 } 194 scheduleReport( 195 Type.SOURCE_NOISED, 196 generateSourceDebugReportBody(source, null), 197 source.getEnrollmentId(), 198 source.getRegistrationOrigin(), 199 dao); 200 } 201 202 /** Schedules Source Storage Limit Debug Report */ scheduleSourceStorageLimitDebugReport( Source source, String limit, IMeasurementDao dao)203 public void scheduleSourceStorageLimitDebugReport( 204 Source source, String limit, IMeasurementDao dao) { 205 if (isSourceDebugFlagDisabled(Type.SOURCE_STORAGE_LIMIT)) { 206 return; 207 } 208 if (isAdTechNotOptIn(source.isDebugReporting(), Type.SOURCE_STORAGE_LIMIT)) { 209 return; 210 } 211 if (getAdIdPermissionFromSource(source) == PermissionState.DENIED 212 || getArDebugPermissionFromSource(source) == PermissionState.DENIED) { 213 LogUtil.d("Skipping debug report %s", Type.SOURCE_STORAGE_LIMIT); 214 return; 215 } 216 scheduleReport( 217 Type.SOURCE_STORAGE_LIMIT, 218 generateSourceDebugReportBody(source, limit), 219 source.getEnrollmentId(), 220 source.getRegistrationOrigin(), 221 dao); 222 } 223 224 /** Schedules the Source Unknown Error Debug Report */ scheduleSourceUnknownErrorDebugReport(Source source, IMeasurementDao dao)225 public void scheduleSourceUnknownErrorDebugReport(Source source, IMeasurementDao dao) { 226 if (isSourceDebugFlagDisabled(Type.SOURCE_UNKNOWN_ERROR)) { 227 return; 228 } 229 if (isAdTechNotOptIn(source.isDebugReporting(), Type.SOURCE_UNKNOWN_ERROR)) { 230 return; 231 } 232 if (getAdIdPermissionFromSource(source) == PermissionState.DENIED 233 || getArDebugPermissionFromSource(source) == PermissionState.DENIED) { 234 LogUtil.d("Skipping debug report %s", Type.SOURCE_UNKNOWN_ERROR); 235 return; 236 } 237 scheduleReport( 238 Type.SOURCE_UNKNOWN_ERROR, 239 generateSourceDebugReportBody(source, null), 240 source.getEnrollmentId(), 241 source.getRegistrationOrigin(), 242 dao); 243 } 244 245 /** 246 * Schedules trigger-no-matching-source and trigger-unknown-error debug reports when trigger 247 * doesn't have related source. 248 */ scheduleTriggerNoMatchingSourceDebugReport( Trigger trigger, IMeasurementDao dao, String type)249 public void scheduleTriggerNoMatchingSourceDebugReport( 250 Trigger trigger, IMeasurementDao dao, String type) throws DatastoreException { 251 if (isTriggerDebugFlagDisabled(type)) { 252 return; 253 } 254 if (isAdTechNotOptIn(trigger.isDebugReporting(), type)) { 255 return; 256 } 257 if (getAdIdPermissionFromTrigger(trigger) == PermissionState.DENIED 258 || getArDebugPermissionFromTrigger(trigger) == PermissionState.DENIED) { 259 LogUtil.d("Skipping trigger debug report %s", type); 260 return; 261 } 262 Pair<UnsignedLong, UnsignedLong> debugKeyPair = 263 new DebugKeyAccessor(dao).getDebugKeysForVerboseTriggerDebugReport(null, trigger); 264 scheduleReport( 265 type, 266 generateTriggerDebugReportBody(null, trigger, null, debugKeyPair, true), 267 trigger.getEnrollmentId(), 268 trigger.getRegistrationOrigin(), 269 dao); 270 } 271 272 /** Schedules Trigger Debug Reports with/without limit, pass in Type for different types. */ scheduleTriggerDebugReport( Source source, Trigger trigger, @Nullable String limit, IMeasurementDao dao, String type)273 public void scheduleTriggerDebugReport( 274 Source source, 275 Trigger trigger, 276 @Nullable String limit, 277 IMeasurementDao dao, 278 String type) 279 throws DatastoreException { 280 if (isTriggerDebugFlagDisabled(type)) { 281 return; 282 } 283 if (isAdTechNotOptIn(trigger.isDebugReporting(), type)) { 284 return; 285 } 286 if (getAdIdPermissionFromTrigger(trigger) == PermissionState.DENIED 287 || getArDebugPermissionFromTrigger(trigger) == PermissionState.DENIED) { 288 LogUtil.d("Skipping trigger debug report %s", type); 289 return; 290 } 291 Pair<UnsignedLong, UnsignedLong> debugKeyPair = 292 new DebugKeyAccessor(dao).getDebugKeysForVerboseTriggerDebugReport(source, trigger); 293 scheduleReport( 294 type, 295 generateTriggerDebugReportBody(source, trigger, limit, debugKeyPair, false), 296 source.getEnrollmentId(), 297 trigger.getRegistrationOrigin(), 298 dao); 299 } 300 301 /** 302 * Schedules Trigger Debug Report with all body fields, Used for trigger-low-priority report and 303 * trigger-event-excessive-reports. 304 */ scheduleTriggerDebugReportWithAllFields( Source source, Trigger trigger, UnsignedLong triggerData, IMeasurementDao dao, String type)305 public void scheduleTriggerDebugReportWithAllFields( 306 Source source, 307 Trigger trigger, 308 UnsignedLong triggerData, 309 IMeasurementDao dao, 310 String type) 311 throws DatastoreException { 312 if (isTriggerDebugFlagDisabled(type)) { 313 return; 314 } 315 if (isAdTechNotOptIn(trigger.isDebugReporting(), type)) { 316 return; 317 } 318 if (getAdIdPermissionFromTrigger(trigger) == PermissionState.DENIED 319 || getArDebugPermissionFromTrigger(trigger) == PermissionState.DENIED) { 320 LogUtil.d("Skipping trigger debug report %s", type); 321 return; 322 } 323 Pair<UnsignedLong, UnsignedLong> debugKeyPair = 324 new DebugKeyAccessor(dao).getDebugKeysForVerboseTriggerDebugReport(source, trigger); 325 scheduleReport( 326 type, 327 generateTriggerDebugReportBodyWithAllFields( 328 source, trigger, triggerData, debugKeyPair), 329 source.getEnrollmentId(), 330 trigger.getRegistrationOrigin(), 331 dao); 332 } 333 334 /** 335 * Schedules the Debug Report to be sent 336 * 337 * @param type The type of the debug report 338 * @param body The body of the debug report 339 * @param enrollmentId Ad Tech enrollment ID 340 * @param registrationOrigin Reporting origin of the report 341 * @param dao Measurement DAO 342 */ scheduleReport( @onNull String type, @NonNull JSONObject body, @NonNull String enrollmentId, @NonNull Uri registrationOrigin, @NonNull IMeasurementDao dao)343 private void scheduleReport( 344 @NonNull String type, 345 @NonNull JSONObject body, 346 @NonNull String enrollmentId, 347 @NonNull Uri registrationOrigin, 348 @NonNull IMeasurementDao dao) { 349 Objects.requireNonNull(type); 350 Objects.requireNonNull(body); 351 Objects.requireNonNull(enrollmentId); 352 Objects.requireNonNull(dao); 353 if (type.isEmpty() || body.length() == 0) { 354 LogUtil.d("Empty debug report found %s", type); 355 return; 356 } 357 if (enrollmentId.isEmpty()) { 358 LogUtil.d("Empty enrollment found %s", type); 359 return; 360 } 361 DebugReport debugReport = 362 new DebugReport.Builder() 363 .setId(UUID.randomUUID().toString()) 364 .setType(type) 365 .setBody(body) 366 .setEnrollmentId(enrollmentId) 367 .setRegistrationOrigin(registrationOrigin) 368 .build(); 369 try { 370 dao.insertDebugReport(debugReport); 371 } catch (DatastoreException e) { 372 LogUtil.e(e, "Failed to insert debug report %s", type); 373 } 374 375 DebugReportingJobService.scheduleIfNeeded( 376 mContext, /*forceSchedule=*/ true, /*isDebugReportApi=*/ true); 377 } 378 379 /** Get AdIdPermission State from Source */ getAdIdPermissionFromSource(Source source)380 private PermissionState getAdIdPermissionFromSource(Source source) { 381 if (source.getPublisherType() == EventSurfaceType.APP) { 382 if (source.hasAdIdPermission()) { 383 return PermissionState.GRANTED; 384 } else { 385 LogUtil.d("Source doesn't have AdId permission"); 386 return PermissionState.DENIED; 387 } 388 } 389 return PermissionState.NONE; 390 } 391 392 /** Get ArDebugPermission State from Source */ getArDebugPermissionFromSource(Source source)393 private PermissionState getArDebugPermissionFromSource(Source source) { 394 if (source.getPublisherType() == EventSurfaceType.WEB) { 395 if (source.hasArDebugPermission()) { 396 return PermissionState.GRANTED; 397 } else { 398 LogUtil.d("Source doesn't have ArDebug permission"); 399 return PermissionState.DENIED; 400 } 401 } 402 return PermissionState.NONE; 403 } 404 getAdIdPermissionFromTrigger(Trigger trigger)405 private PermissionState getAdIdPermissionFromTrigger(Trigger trigger) { 406 if (trigger.getDestinationType() == EventSurfaceType.APP) { 407 if (trigger.hasAdIdPermission()) { 408 return PermissionState.GRANTED; 409 } else { 410 LogUtil.d("Trigger doesn't have AdId permission"); 411 return PermissionState.DENIED; 412 } 413 } 414 return PermissionState.NONE; 415 } 416 getArDebugPermissionFromTrigger(Trigger trigger)417 private PermissionState getArDebugPermissionFromTrigger(Trigger trigger) { 418 if (trigger.getDestinationType() == EventSurfaceType.WEB) { 419 if (trigger.hasArDebugPermission()) { 420 return PermissionState.GRANTED; 421 } else { 422 LogUtil.d("Trigger doesn't have ArDebug permission"); 423 return PermissionState.DENIED; 424 } 425 } 426 return PermissionState.NONE; 427 } 428 429 /** Get is Ad tech not op-in and log */ isAdTechNotOptIn(boolean optIn, String type)430 private boolean isAdTechNotOptIn(boolean optIn, String type) { 431 if (!optIn) { 432 LogUtil.d("Ad-tech not opt-in. Skipping debug report %s", type); 433 } 434 return !optIn; 435 } 436 437 /** Generates source debug report body */ generateSourceDebugReportBody( @onNull Source source, @Nullable String limit)438 private JSONObject generateSourceDebugReportBody( 439 @NonNull Source source, @Nullable String limit) { 440 JSONObject body = new JSONObject(); 441 try { 442 body.put(Body.SOURCE_EVENT_ID, source.getEventId().toString()); 443 body.put(Body.ATTRIBUTION_DESTINATION, generateSourceDestinations(source)); 444 body.put(Body.SOURCE_SITE, generateSourceSite(source)); 445 body.put(Body.LIMIT, limit); 446 body.put(Body.SOURCE_DEBUG_KEY, source.getDebugKey()); 447 } catch (JSONException e) { 448 LogUtil.e(e, "Json error while generating source debug report body."); 449 } 450 return body; 451 } 452 generateSourceDestinations(Source source)453 private static Object generateSourceDestinations(Source source) throws JSONException { 454 List<Uri> destinations = new ArrayList<>(); 455 Optional.ofNullable(source.getAppDestinations()).ifPresent(destinations::addAll); 456 List<Uri> webDestinations = source.getWebDestinations(); 457 if (webDestinations != null) { 458 for (Uri webDestination : webDestinations) { 459 Optional<Uri> webUri = Web.topPrivateDomainAndScheme(webDestination); 460 webUri.ifPresent(destinations::add); 461 } 462 } 463 return ReportUtil.serializeAttributionDestinations(destinations); 464 } 465 generateSourceSite(Source source)466 private static Uri generateSourceSite(Source source) { 467 if (source.getPublisherType() == EventSurfaceType.APP) { 468 return source.getPublisher(); 469 } else { 470 return Web.topPrivateDomainAndScheme(source.getPublisher()).orElse(null); 471 } 472 } 473 474 /** Generates trigger debug report body */ generateTriggerDebugReportBody( @ullable Source source, @NonNull Trigger trigger, @Nullable String limit, @NonNull Pair<UnsignedLong, UnsignedLong> debugKeyPair, boolean isTriggerNoMatchingSource)475 private JSONObject generateTriggerDebugReportBody( 476 @Nullable Source source, 477 @NonNull Trigger trigger, 478 @Nullable String limit, 479 @NonNull Pair<UnsignedLong, UnsignedLong> debugKeyPair, 480 boolean isTriggerNoMatchingSource) { 481 JSONObject body = new JSONObject(); 482 try { 483 body.put(Body.ATTRIBUTION_DESTINATION, trigger.getAttributionDestinationBaseUri()); 484 body.put(Body.TRIGGER_DEBUG_KEY, debugKeyPair.second); 485 if (isTriggerNoMatchingSource) { 486 return body; 487 } 488 body.put(Body.LIMIT, limit); 489 body.put(Body.SOURCE_DEBUG_KEY, debugKeyPair.first); 490 body.put(Body.SOURCE_EVENT_ID, source.getEventId().toString()); 491 body.put(Body.SOURCE_SITE, generateSourceSite(source)); 492 } catch (JSONException e) { 493 LogUtil.e(e, "Json error while generating trigger debug report body."); 494 } 495 return body; 496 } 497 498 /** 499 * Generates trigger debug report body with all fields in event-level attribution report. Used 500 * for trigger-low-priority, trigger-event-excessive-reports debug reports. 501 */ generateTriggerDebugReportBodyWithAllFields( @onNull Source source, @NonNull Trigger trigger, @Nullable UnsignedLong triggerData, @NonNull Pair<UnsignedLong, UnsignedLong> debugKeyPair)502 private JSONObject generateTriggerDebugReportBodyWithAllFields( 503 @NonNull Source source, 504 @NonNull Trigger trigger, 505 @Nullable UnsignedLong triggerData, 506 @NonNull Pair<UnsignedLong, UnsignedLong> debugKeyPair) { 507 JSONObject body = new JSONObject(); 508 try { 509 body.put(Body.ATTRIBUTION_DESTINATION, trigger.getAttributionDestinationBaseUri()); 510 body.put( 511 Body.SCHEDULED_REPORT_TIME, 512 String.valueOf( 513 TimeUnit.MILLISECONDS.toSeconds( 514 mEventReportWindowCalcDelegate.getReportingTime( 515 source, 516 trigger.getTriggerTime(), 517 trigger.getDestinationType())))); 518 body.put(Body.SOURCE_EVENT_ID, source.getEventId()); 519 body.put(Body.SOURCE_TYPE, source.getSourceType().getValue()); 520 body.put( 521 Body.RANDOMIZED_TRIGGER_RATE, 522 mSourceNoiseHandler.getRandomAttributionProbability(source)); 523 if (triggerData != null) { 524 body.put(Body.TRIGGER_DATA, triggerData.toString()); 525 } 526 if (debugKeyPair.first != null) { 527 body.put(Body.SOURCE_DEBUG_KEY, debugKeyPair.first); 528 } 529 if (debugKeyPair.second != null) { 530 body.put(Body.TRIGGER_DEBUG_KEY, debugKeyPair.second); 531 } 532 } catch (JSONException e) { 533 LogUtil.e(e, "Json error while generating trigger debug report body with all fields."); 534 } 535 return body; 536 } 537 538 /** Checks flags for source debug reports. */ isSourceDebugFlagDisabled(String type)539 private boolean isSourceDebugFlagDisabled(String type) { 540 if (!mFlags.getMeasurementEnableDebugReport() 541 || !mFlags.getMeasurementEnableSourceDebugReport()) { 542 LogUtil.d("Source flag is disabled for %s debug report", type); 543 return true; 544 } 545 return false; 546 } 547 548 /** Checks flags for trigger debug reports. */ isTriggerDebugFlagDisabled(String type)549 private boolean isTriggerDebugFlagDisabled(String type) { 550 if (!mFlags.getMeasurementEnableDebugReport() 551 || !mFlags.getMeasurementEnableTriggerDebugReport()) { 552 LogUtil.d("Trigger flag is disabled for %s debug report", type); 553 return true; 554 } 555 return false; 556 } 557 } 558