• 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.stats;
18 
19 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED;
20 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_CLASS__UNKNOWN;
21 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED;
22 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_BACK_COMPAT_EPOCH_COMPUTATION_CLASSIFIER_REPORTED;
23 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_BACK_COMPAT_GET_TOPICS_REPORTED;
24 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED;
25 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_EPOCH_COMPUTATION_CLASSIFIER_REPORTED;
26 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_EPOCH_COMPUTATION_GET_TOP_TOPICS_REPORTED;
27 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED;
28 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_GET_TOPICS_REPORTED;
29 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MEASUREMENT_DEBUG_KEYS;
30 import static com.android.adservices.service.stats.AdServicesStatsLog.BACKGROUND_FETCH_PROCESS_REPORTED;
31 import static com.android.adservices.service.stats.AdServicesStatsLog.RUN_AD_BIDDING_PER_CA_PROCESS_REPORTED;
32 import static com.android.adservices.service.stats.AdServicesStatsLog.RUN_AD_BIDDING_PROCESS_REPORTED;
33 import static com.android.adservices.service.stats.AdServicesStatsLog.RUN_AD_SCORING_PROCESS_REPORTED;
34 import static com.android.adservices.service.stats.AdServicesStatsLog.RUN_AD_SELECTION_PROCESS_REPORTED;
35 import static com.android.adservices.service.stats.AdServicesStatsLog.UPDATE_CUSTOM_AUDIENCE_PROCESS_REPORTED;
36 
37 import android.annotation.NonNull;
38 import android.util.proto.ProtoOutputStream;
39 
40 import com.android.adservices.errorlogging.AdServicesErrorStats;
41 import com.android.adservices.errorlogging.StatsdAdServicesErrorLogger;
42 import com.android.adservices.service.Flags;
43 import com.android.adservices.service.FlagsFactory;
44 import com.android.adservices.spe.stats.ExecutionReportedStats;
45 import com.android.internal.annotations.GuardedBy;
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.modules.utils.build.SdkLevel;
48 
49 import javax.annotation.concurrent.ThreadSafe;
50 
51 /**
52  * {@link AdServicesLogger} that log stats to StatsD and {@link StatsdAdServicesErrorLogger} that
53  * logs error stats to Statsd.
54  */
55 @ThreadSafe
56 public class StatsdAdServicesLogger implements AdServicesLogger, StatsdAdServicesErrorLogger {
57     private static final int AD_SERVICES_TOPIC_IDS_FIELD_ID = 1;
58 
59     @GuardedBy("SINGLETON_LOCK")
60     private static volatile StatsdAdServicesLogger sStatsdAdServicesLogger;
61 
62     private static final Object SINGLETON_LOCK = new Object();
63 
64     @NonNull private final Flags mFlags;
65 
66     @VisibleForTesting
StatsdAdServicesLogger(@onNull Flags mFlags)67     protected StatsdAdServicesLogger(@NonNull Flags mFlags) {
68         this.mFlags = mFlags;
69     }
70 
71     /** Returns an instance of {@link StatsdAdServicesLogger}. */
getInstance()72     public static StatsdAdServicesLogger getInstance() {
73         if (sStatsdAdServicesLogger == null) {
74             synchronized (SINGLETON_LOCK) {
75                 if (sStatsdAdServicesLogger == null) {
76                     sStatsdAdServicesLogger = new StatsdAdServicesLogger(FlagsFactory.getFlags());
77                 }
78             }
79         }
80         return sStatsdAdServicesLogger;
81     }
82 
83     /** log method for measurement reporting. */
logMeasurementReports(MeasurementReportsStats measurementReportsStats)84     public void logMeasurementReports(MeasurementReportsStats measurementReportsStats) {
85         AdServicesStatsLog.write(
86                 measurementReportsStats.getCode(),
87                 measurementReportsStats.getType(),
88                 measurementReportsStats.getResultCode(),
89                 measurementReportsStats.getFailureType(),
90                 measurementReportsStats.getUploadMethod(),
91                 measurementReportsStats.getReportingDelay());
92     }
93 
94     /** log method for API call stats. */
logApiCallStats(ApiCallStats apiCallStats)95     public void logApiCallStats(ApiCallStats apiCallStats) {
96         AdServicesStatsLog.write(
97                 apiCallStats.getCode(),
98                 apiCallStats.getApiClass(),
99                 apiCallStats.getApiName(),
100                 apiCallStats.getAppPackageName(),
101                 apiCallStats.getSdkPackageName(),
102                 apiCallStats.getLatencyMillisecond(),
103                 apiCallStats.getResultCode());
104     }
105 
106     /** log method for UI stats. */
logUIStats(UIStats uiStats)107     public void logUIStats(UIStats uiStats) {
108         AdServicesStatsLog.write(uiStats.getCode(), uiStats.getRegion(), uiStats.getAction());
109     }
110 
111     @Override
logFledgeApiCallStats(int apiName, int resultCode, int latencyMs)112     public void logFledgeApiCallStats(int apiName, int resultCode, int latencyMs) {
113         AdServicesStatsLog.write(
114                 AD_SERVICES_API_CALLED,
115                 AD_SERVICES_API_CALLED__API_CLASS__UNKNOWN,
116                 apiName,
117                 "",
118                 "",
119                 latencyMs,
120                 resultCode);
121     }
122 
123     @Override
logMeasurementRegistrationsResponseSize( MeasurementRegistrationResponseStats stats)124     public void logMeasurementRegistrationsResponseSize(
125             MeasurementRegistrationResponseStats stats) {
126         AdServicesStatsLog.write(
127                 stats.getCode(),
128                 stats.getRegistrationType(),
129                 stats.getResponseSize(),
130                 stats.getAdTechDomain(),
131                 stats.getInteractionType(),
132                 stats.getSurfaceType(),
133                 stats.getRegistrationStatus(),
134                 stats.getFailureType(),
135                 stats.getRegistrationDelay());
136     }
137 
138     @Override
logRunAdSelectionProcessReportedStats(RunAdSelectionProcessReportedStats stats)139     public void logRunAdSelectionProcessReportedStats(RunAdSelectionProcessReportedStats stats) {
140         AdServicesStatsLog.write(
141                 RUN_AD_SELECTION_PROCESS_REPORTED,
142                 stats.getIsRemarketingAdsWon(),
143                 stats.getDBAdSelectionSizeInBytes(),
144                 stats.getPersistAdSelectionLatencyInMillis(),
145                 stats.getPersistAdSelectionResultCode(),
146                 stats.getRunAdSelectionLatencyInMillis(),
147                 stats.getRunAdSelectionResultCode());
148     }
149 
150     @Override
logRunAdBiddingProcessReportedStats(RunAdBiddingProcessReportedStats stats)151     public void logRunAdBiddingProcessReportedStats(RunAdBiddingProcessReportedStats stats) {
152         AdServicesStatsLog.write(
153                 RUN_AD_BIDDING_PROCESS_REPORTED,
154                 stats.getGetBuyersCustomAudienceLatencyInMills(),
155                 stats.getGetBuyersCustomAudienceResultCode(),
156                 stats.getNumBuyersRequested(),
157                 stats.getNumBuyersFetched(),
158                 stats.getNumOfAdsEnteringBidding(),
159                 stats.getNumOfCasEnteringBidding(),
160                 stats.getNumOfCasPostBidding(),
161                 stats.getRatioOfCasSelectingRmktAds(),
162                 stats.getRunAdBiddingLatencyInMillis(),
163                 stats.getRunAdBiddingResultCode(),
164                 stats.getTotalAdBiddingStageLatencyInMillis());
165     }
166 
167     @Override
logRunAdScoringProcessReportedStats(RunAdScoringProcessReportedStats stats)168     public void logRunAdScoringProcessReportedStats(RunAdScoringProcessReportedStats stats) {
169         AdServicesStatsLog.write(
170                 RUN_AD_SCORING_PROCESS_REPORTED,
171                 stats.getGetAdSelectionLogicLatencyInMillis(),
172                 stats.getGetAdSelectionLogicResultCode(),
173                 stats.getGetAdSelectionLogicScriptType(),
174                 stats.getFetchedAdSelectionLogicScriptSizeInBytes(),
175                 stats.getGetTrustedScoringSignalsLatencyInMillis(),
176                 stats.getGetTrustedScoringSignalsResultCode(),
177                 stats.getFetchedTrustedScoringSignalsDataSizeInBytes(),
178                 stats.getScoreAdsLatencyInMillis(),
179                 stats.getGetAdScoresLatencyInMillis(),
180                 stats.getGetAdScoresResultCode(),
181                 stats.getNumOfCasEnteringScoring(),
182                 stats.getNumOfRemarketingAdsEnteringScoring(),
183                 stats.getNumOfContextualAdsEnteringScoring(),
184                 stats.getRunAdScoringLatencyInMillis(),
185                 stats.getRunAdScoringResultCode());
186     }
187 
188     @Override
logRunAdBiddingPerCAProcessReportedStats( RunAdBiddingPerCAProcessReportedStats stats)189     public void logRunAdBiddingPerCAProcessReportedStats(
190             RunAdBiddingPerCAProcessReportedStats stats) {
191         AdServicesStatsLog.write(
192                 RUN_AD_BIDDING_PER_CA_PROCESS_REPORTED,
193                 stats.getNumOfAdsForBidding(),
194                 stats.getRunAdBiddingPerCaLatencyInMillis(),
195                 stats.getRunAdBiddingPerCaResultCode(),
196                 stats.getGetBuyerDecisionLogicLatencyInMillis(),
197                 stats.getGetBuyerDecisionLogicResultCode(),
198                 stats.getBuyerDecisionLogicScriptType(),
199                 stats.getFetchedBuyerDecisionLogicScriptSizeInBytes(),
200                 stats.getNumOfKeysOfTrustedBiddingSignals(),
201                 stats.getFetchedTrustedBiddingSignalsDataSizeInBytes(),
202                 stats.getGetTrustedBiddingSignalsLatencyInMillis(),
203                 stats.getGetTrustedBiddingSignalsResultCode(),
204                 stats.getGenerateBidsLatencyInMillis(),
205                 stats.getRunBiddingLatencyInMillis(),
206                 stats.getRunBiddingResultCode());
207     }
208 
209     @Override
logBackgroundFetchProcessReportedStats(BackgroundFetchProcessReportedStats stats)210     public void logBackgroundFetchProcessReportedStats(BackgroundFetchProcessReportedStats stats) {
211         AdServicesStatsLog.write(
212                 BACKGROUND_FETCH_PROCESS_REPORTED,
213                 stats.getLatencyInMillis(),
214                 stats.getNumOfEligibleToUpdateCas(),
215                 stats.getResultCode());
216     }
217 
218     @Override
logUpdateCustomAudienceProcessReportedStats( UpdateCustomAudienceProcessReportedStats stats)219     public void logUpdateCustomAudienceProcessReportedStats(
220             UpdateCustomAudienceProcessReportedStats stats) {
221         AdServicesStatsLog.write(
222                 UPDATE_CUSTOM_AUDIENCE_PROCESS_REPORTED,
223                 stats.getLatencyInMills(),
224                 stats.getResultCode(),
225                 stats.getDataSizeOfAdsInBytes(),
226                 stats.getNumOfAds());
227     }
228 
229     @Override
logGetTopicsReportedStats(GetTopicsReportedStats stats)230     public void logGetTopicsReportedStats(GetTopicsReportedStats stats) {
231         boolean isCompatLoggingEnabled = !mFlags.getCompatLoggingKillSwitch();
232         if (isCompatLoggingEnabled) {
233             AdServicesStatsLog.write(
234                     AD_SERVICES_BACK_COMPAT_GET_TOPICS_REPORTED,
235                     // TODO(b/266626836) Add topic ids logging once long term solution is identified
236                     stats.getDuplicateTopicCount(),
237                     stats.getFilteredBlockedTopicCount(),
238                     stats.getTopicIdsCount());
239         }
240 
241         // This atom can only be logged on T+ due to usage of repeated fields. See go/rbc-ww-logging
242         // for why we are temporarily double logging on T+.
243         if (SdkLevel.isAtLeastT()) {
244             AdServicesStatsLog.write(
245                     AD_SERVICES_GET_TOPICS_REPORTED,
246                     new int[] {}, // TODO(b/256649873): Log empty list until long term solution.
247                     stats.getDuplicateTopicCount(),
248                     stats.getFilteredBlockedTopicCount(),
249                     stats.getTopicIdsCount());
250         }
251     }
252 
253     @Override
logEpochComputationGetTopTopicsStats(EpochComputationGetTopTopicsStats stats)254     public void logEpochComputationGetTopTopicsStats(EpochComputationGetTopTopicsStats stats) {
255         AdServicesStatsLog.write(
256                 AD_SERVICES_EPOCH_COMPUTATION_GET_TOP_TOPICS_REPORTED,
257                 stats.getTopTopicCount(),
258                 stats.getPaddedRandomTopicsCount(),
259                 stats.getAppsConsideredCount(),
260                 stats.getSdksConsideredCount());
261     }
262 
263     @Override
logEpochComputationClassifierStats(EpochComputationClassifierStats stats)264     public void logEpochComputationClassifierStats(EpochComputationClassifierStats stats) {
265         int[] topicIds = stats.getTopicIds().stream().mapToInt(Integer::intValue).toArray();
266 
267         boolean isCompatLoggingEnabled = !mFlags.getCompatLoggingKillSwitch();
268         if (isCompatLoggingEnabled) {
269             long modeBytesFieldId =
270                     ProtoOutputStream.FIELD_COUNT_REPEATED // topic_ids field is repeated.
271                             // topic_id is represented by int32 type.
272                             | ProtoOutputStream.FIELD_TYPE_INT32
273                             // Field ID of topic_ids field in AdServicesTopicIds proto.
274                             | AD_SERVICES_TOPIC_IDS_FIELD_ID;
275 
276             AdServicesStatsLog.write(
277                     AD_SERVICES_BACK_COMPAT_EPOCH_COMPUTATION_CLASSIFIER_REPORTED,
278                     toBytes(modeBytesFieldId, topicIds),
279                     stats.getBuildId(),
280                     stats.getAssetVersion(),
281                     stats.getClassifierType().getCompatLoggingValue(),
282                     stats.getOnDeviceClassifierStatus().getCompatLoggingValue(),
283                     stats.getPrecomputedClassifierStatus().getCompatLoggingValue());
284         }
285 
286         // This atom can only be logged on T+ due to usage of repeated fields. See go/rbc-ww-logging
287         // for why we are temporarily double logging on T+.
288         if (SdkLevel.isAtLeastT()) {
289             AdServicesStatsLog.write(
290                     AD_SERVICES_EPOCH_COMPUTATION_CLASSIFIER_REPORTED,
291                     topicIds,
292                     stats.getBuildId(),
293                     stats.getAssetVersion(),
294                     stats.getClassifierType().getLoggingValue(),
295                     stats.getOnDeviceClassifierStatus().getLoggingValue(),
296                     stats.getPrecomputedClassifierStatus().getLoggingValue());
297         }
298     }
299 
300     @Override
logMeasurementDebugKeysMatch(MsmtDebugKeysMatchStats stats)301     public void logMeasurementDebugKeysMatch(MsmtDebugKeysMatchStats stats) {
302         AdServicesStatsLog.write(
303                 AD_SERVICES_MEASUREMENT_DEBUG_KEYS,
304                 stats.getAdTechEnrollmentId(),
305                 stats.getAttributionType(),
306                 stats.isMatched(),
307                 stats.getDebugJoinKeyHashedValue(),
308                 stats.getDebugJoinKeyHashLimit());
309     }
310 
311     @Override
logAdServicesError(AdServicesErrorStats stats)312     public void logAdServicesError(AdServicesErrorStats stats) {
313         AdServicesStatsLog.write(
314                 AD_SERVICES_ERROR_REPORTED,
315                 stats.getErrorCode(),
316                 stats.getPpapiName(),
317                 stats.getClassName(),
318                 stats.getMethodName(),
319                 stats.getLineNumber(),
320                 stats.getLastObservedExceptionName());
321     }
322 
323     /** Logging method for AdServices background job execution stats. */
logExecutionReportedStats(ExecutionReportedStats stats)324     public void logExecutionReportedStats(ExecutionReportedStats stats) {
325         AdServicesStatsLog.write(
326                 AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED,
327                 stats.getJobId(),
328                 stats.getExecutionLatencyMs(),
329                 stats.getExecutionPeriodMinute(),
330                 stats.getExecutionResultCode(),
331                 stats.getStopReason());
332     }
333 
334     /** log method for measurement attribution. */
logMeasurementAttributionStats( MeasurementAttributionStats measurementAttributionStats)335     public void logMeasurementAttributionStats(
336             MeasurementAttributionStats measurementAttributionStats) {
337         AdServicesStatsLog.write(
338                 measurementAttributionStats.getCode(),
339                 measurementAttributionStats.getSourceType(),
340                 measurementAttributionStats.getSurfaceType(),
341                 measurementAttributionStats.getResult(),
342                 measurementAttributionStats.getFailureType(),
343                 measurementAttributionStats.isSourceDerived(),
344                 measurementAttributionStats.isInstallAttribution(),
345                 measurementAttributionStats.getAttributionDelay());
346     }
347 
348     /** log method for measurement wipeout. */
logMeasurementWipeoutStats(MeasurementWipeoutStats measurementWipeoutStats)349     public void logMeasurementWipeoutStats(MeasurementWipeoutStats measurementWipeoutStats) {
350         AdServicesStatsLog.write(
351                 measurementWipeoutStats.getCode(), measurementWipeoutStats.getWipeoutType());
352     }
353 
354     /** log method for measurement attribution. */
logMeasurementDelayedSourceRegistrationStats( MeasurementDelayedSourceRegistrationStats measurementDelayedSourceRegistrationStats)355     public void logMeasurementDelayedSourceRegistrationStats(
356             MeasurementDelayedSourceRegistrationStats measurementDelayedSourceRegistrationStats) {
357         AdServicesStatsLog.write(
358                 measurementDelayedSourceRegistrationStats.getCode(),
359                 measurementDelayedSourceRegistrationStats.getRegistrationStatus(),
360                 measurementDelayedSourceRegistrationStats.getRegistrationDelay());
361     }
362 
363     /** log method for consent migrations. */
logConsentMigrationStats(ConsentMigrationStats stats)364     public void logConsentMigrationStats(ConsentMigrationStats stats) {
365         if (mFlags.getAdservicesConsentMigrationLoggingEnabled()) {
366             AdServicesStatsLog.write(
367                     AD_SERVICES_CONSENT_MIGRATED,
368                     stats.getMsmtConsent(),
369                     stats.getTopicsConsent(),
370                     stats.getFledgeConsent(),
371                     stats.getDefaultConsent(),
372                     stats.getMigrationType().getMigrationTypeValue(),
373                     stats.getRegion(),
374                     stats.getMigrationStatus().getMigrationStatusValue());
375         }
376     }
377 
378     @NonNull
toBytes(long fieldId, @NonNull int[] values)379     private byte[] toBytes(long fieldId, @NonNull int[] values) {
380         ProtoOutputStream protoOutputStream = new ProtoOutputStream();
381         for (int value : values) {
382             protoOutputStream.write(fieldId, value);
383         }
384         return protoOutputStream.getBytes();
385     }
386 }
387