• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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