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.noising; 18 19 import com.android.adservices.service.measurement.ReportSpec; 20 import com.android.internal.annotations.VisibleForTesting; 21 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.List; 25 import java.util.Random; 26 27 /** 28 * Util class for generating impression noise 29 */ 30 public final class ImpressionNoiseUtil { 31 32 /** 33 * This is used in the scenario where both app and web destinations are available with the 34 * {@link com.android.adservices.service.measurement.Source} and the source is of {@link 35 * com.android.adservices.service.measurement.Source.SourceType#EVENT} type and post-install 36 * detection is enabled (cooldown window being available). It's a special case because in this 37 * condition an extra early window is added only if install detection is enabled (install 38 * cool-down window is available) and only app conversions are relevant in that window. 39 * 40 * <p>Reading guide - The outermost array signifies different states of reporting - one of them 41 * will be picked at a time. The middle array holds reports in 1st and 2nd window, so there will 42 * be either 0 elements (no conversions in either window), 1 element (a conversion report in one 43 * of the windows) and 2 elements (conversions in both windows). The innermost array represents 44 * a single report. 3 elements in the innermost array are trigger metadata (0 - trigger1 or 1 - 45 * trigger2), window index (0 - window1, 1 - window2) and destination type (0 - app or 1 - web). 46 * 47 * <p>E.g. The element at index 5 is {{0, 1, 0}, {0, 1, 0}}, it means 2 conversions with trigger 48 * metadata as 0 in window2 (1) of app destination type(0). 49 */ 50 public static final int[][][] DUAL_DESTINATION_POST_INSTALL_FAKE_REPORT_CONFIG = 51 new int[][][] { 52 // window1 - no conversion, window 2 - no conversion 53 {}, 54 // window1 - no conversion, window 2 - 1 conversion with metadata 0 55 {{0, 1, 0}}, 56 {{0, 1, 1}}, 57 // window1 - no conversion, window 2 - 1 conversion with metadata 1 58 {{1, 1, 0}}, 59 {{1, 1, 1}}, 60 // window1 - no conversion, window 2 - 2 conversions with metadata 0 and 0 61 {{0, 1, 0}, {0, 1, 0}}, 62 {{0, 1, 0}, {0, 1, 1}}, 63 // window1 - no conversion, window 2 - 2 conversions with metadata 0 and 1 64 {{0, 1, 0}, {1, 1, 0}}, 65 {{0, 1, 0}, {1, 1, 1}}, 66 {{0, 1, 1}, {1, 1, 0}}, 67 // window1 - no conversion, window 2 - 2 conversions with metadata 1 and 1 68 {{1, 1, 0}, {1, 1, 0}}, 69 {{1, 1, 0}, {1, 1, 1}}, 70 // window1 - 1 app conversion with metadata 0, window 2 - no conversion 71 {{0, 0, 0}}, 72 // window1 - 1 app conversion with metadata 1, window 2 - no conversion 73 {{1, 0, 0}}, 74 // window1 - 2 conversions with metadata 0 and 0, window 2 - no conversion 75 {{0, 0, 0}, {0, 0, 0}}, 76 // window1 - 2 app conversions with metadata 0 and 1, window 2 - no conversion 77 {{0, 0, 0}, {1, 0, 0}}, 78 // window1 - 2 app conversions with metadata 1 and 1, window 2 - no conversion 79 {{1, 0, 0}, {1, 0, 0}}, 80 // window1 - 1 app conversion with metadata 0, window 2 - 1 conversion with 81 // metadata 0 82 {{0, 0, 0}, {0, 1, 0}}, 83 {{0, 0, 0}, {0, 1, 1}}, 84 // window1 - 1 app conversion with metadata 0, window 2 - 1 conversion with 85 // metadata 1 86 {{0, 0, 0}, {1, 1, 0}}, 87 {{0, 0, 0}, {1, 1, 1}}, 88 // window1 - 1 app conversion with metadata 1, window 2 - 1 conversions with 89 // metadata 0 90 {{1, 0, 0}, {0, 1, 0}}, 91 {{1, 0, 0}, {0, 1, 1}}, 92 // window1 - 1 app conversion with metadata 1, window 2 - 1 conversions with 93 // metadata 1 94 {{1, 0, 0}, {1, 1, 0}}, 95 {{1, 0, 0}, {1, 1, 1}} 96 }; 97 ImpressionNoiseUtil()98 private ImpressionNoiseUtil() {} 99 100 /** 101 * Randomly generate report configs based on noise params 102 * 103 * @param noiseParams Noise parameters to use for state generation 104 * @param rand random number generator 105 * @return list of reporting configs 106 */ selectRandomStateAndGenerateReportConfigs( ImpressionNoiseParams noiseParams, Random rand)107 public static List<int[]> selectRandomStateAndGenerateReportConfigs( 108 ImpressionNoiseParams noiseParams, Random rand) { 109 // Get total possible combinations 110 int numCombinations = 111 Combinatorics.getNumberOfStarsAndBarsSequences( 112 /*numStars=*/ noiseParams.getReportCount(), 113 /*numBars=*/ noiseParams.getTriggerDataCardinality() 114 * noiseParams.getReportingWindowCount() 115 * noiseParams.getDestinationTypeMultiplier()); 116 // Choose a sequence index 117 int sequenceIndex = rand.nextInt(numCombinations); 118 return getReportConfigsForSequenceIndex(noiseParams, sequenceIndex); 119 } 120 121 @VisibleForTesting getReportConfigsForSequenceIndex( ImpressionNoiseParams noiseParams, int sequenceIndex)122 static List<int[]> getReportConfigsForSequenceIndex( 123 ImpressionNoiseParams noiseParams, int sequenceIndex) { 124 List<int[]> reportConfigs = new ArrayList<>(); 125 // Get the configuration for the sequenceIndex 126 int[] starIndices = Combinatorics.getStarIndices( 127 /*numStars=*/noiseParams.getReportCount(), 128 /*sequenceIndex=*/sequenceIndex); 129 int[] barsPrecedingEachStar = Combinatorics.getBarsPrecedingEachStar(starIndices); 130 // Generate fake reports 131 // Stars: number of reports 132 // Bars: (Number of windows) * (Trigger data cardinality) * (Destination multiplier) 133 for (int numBars : barsPrecedingEachStar) { 134 if (numBars == 0) { 135 continue; 136 } 137 138 // Extract bits for trigger data, destination type and windowIndex from encoded numBars 139 int[] reportConfig = createReportingConfig(numBars, noiseParams); 140 reportConfigs.add(reportConfig); 141 } 142 return reportConfigs; 143 } 144 145 /** 146 * Extract bits for trigger data, destination type and windowIndex from encoded numBars. 147 * 148 * @param numBars data encoding triggerData, destinationType and window index 149 * @param noiseParams noise params 150 * @return array having triggerData, destinationType and windowIndex 151 */ createReportingConfig(int numBars, ImpressionNoiseParams noiseParams)152 private static int[] createReportingConfig(int numBars, ImpressionNoiseParams noiseParams) { 153 int triggerData = (numBars - 1) % noiseParams.getTriggerDataCardinality(); 154 int remainingData = (numBars - 1) / noiseParams.getTriggerDataCardinality(); 155 156 int reportingWindowIndex = remainingData % noiseParams.getReportingWindowCount(); 157 int destinationTypeIndex = remainingData / noiseParams.getReportingWindowCount(); 158 return new int[] {triggerData, reportingWindowIndex, destinationTypeIndex}; 159 } 160 161 /** 162 * Randomly generate report configs based on noise params 163 * 164 * @param reportSpec Report spec to use for state generation 165 * @param destinationMultiplier destination multiplier 166 * @param rand random number generator 167 * @return list of reporting configs 168 */ selectFlexEventReportRandomStateAndGenerateReportConfigs( ReportSpec reportSpec, int destinationMultiplier, Random rand)169 public static List<int[]> selectFlexEventReportRandomStateAndGenerateReportConfigs( 170 ReportSpec reportSpec, int destinationMultiplier, Random rand) { 171 172 int[][] params = reportSpec.getPrivacyParamsForComputation(); 173 for (int i = 0; i < params[1].length; i++) { 174 params[1][i] *= destinationMultiplier; 175 } 176 int numStates = Combinatorics.getNumStatesFlexAPI(params[0][0], params[1], params[2]); 177 int sequenceIndex = rand.nextInt(numStates); 178 List<Combinatorics.AtomReportState> rawFakeReports = 179 Combinatorics.getReportSetBasedOnRank( 180 params[0][0], params[1], params[2], sequenceIndex, new HashMap<>()); 181 List<int[]> fakeReportConfigs = new ArrayList<>(); 182 for (Combinatorics.AtomReportState rawFakeReport : rawFakeReports) { 183 int[] fakeReportConfig = new int[3]; 184 fakeReportConfig[0] = rawFakeReport.getTriggerDataType(); 185 fakeReportConfig[1] = (rawFakeReport.getWindowIndex()) / destinationMultiplier; 186 fakeReportConfig[2] = (rawFakeReport.getWindowIndex()) % destinationMultiplier; 187 fakeReportConfigs.add(fakeReportConfig); 188 } 189 return fakeReportConfigs; 190 } 191 } 192