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