1 /*
2  * Copyright (C) 2024 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.adselection;
18 
19 import static android.adservices.customaudience.CustomAudience.FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS;
20 
21 import android.adservices.common.AdTechIdentifier;
22 import android.annotation.NonNull;
23 
24 import com.android.adservices.data.customaudience.DBCustomAudience;
25 import com.android.adservices.data.signals.DBEncodedPayload;
26 import com.android.adservices.service.proto.bidding_auction_servers.BiddingAuctionServers;
27 import com.android.adservices.service.stats.BuyerInputGeneratorIntermediateStats;
28 
29 import com.google.common.base.Strings;
30 import com.google.common.collect.ImmutableList;
31 import com.google.protobuf.ByteString;
32 
33 import java.util.List;
34 import java.util.Map;
35 import java.util.stream.Collectors;
36 
37 public class CompressedBuyerInputCreatorHelper {
38     protected static final String EMPTY_USER_BIDDING_SIGNALS = "{}";
39     private final AuctionServerPayloadMetricsStrategy mAuctionServerPayloadMetricsStrategy;
40     private final boolean mPasExtendedMetricsEnabled;
41     private final boolean mEnableOmitAds;
42 
43     private int mEncodedSignalsCount = 0;
44     private int mEncodedSignalsTotalSizeInBytes = 0;
45     private int mEncodedSignalsMaxSizeInBytes = 0;
46     private int mEncodedSignalsMinSizeInBytes = Integer.MAX_VALUE;
47 
CompressedBuyerInputCreatorHelper( AuctionServerPayloadMetricsStrategy auctionServerPayloadMetricsStrategy, boolean pasExtendedMetricsEnabled, boolean enableOmitAds)48     public CompressedBuyerInputCreatorHelper(
49             AuctionServerPayloadMetricsStrategy auctionServerPayloadMetricsStrategy,
50             boolean pasExtendedMetricsEnabled,
51             boolean enableOmitAds) {
52         mAuctionServerPayloadMetricsStrategy = auctionServerPayloadMetricsStrategy;
53         mPasExtendedMetricsEnabled = pasExtendedMetricsEnabled;
54         mEnableOmitAds = enableOmitAds;
55     }
56 
57     /**
58      * Builds a bidding and auction server custom audience proto from a {@link DBCustomAudience}.
59      */
buildCustomAudienceProtoFrom( DBCustomAudience customAudience)60     public BiddingAuctionServers.BuyerInput.CustomAudience buildCustomAudienceProtoFrom(
61             DBCustomAudience customAudience) {
62         BiddingAuctionServers.BuyerInput.CustomAudience.Builder customAudienceBuilder =
63                 BiddingAuctionServers.BuyerInput.CustomAudience.newBuilder();
64 
65         customAudienceBuilder
66                 .setName(customAudience.getName())
67                 .setOwner(customAudience.getOwner())
68                 .setUserBiddingSignals(getUserBiddingSignals(customAudience))
69                 .addAllBiddingSignalsKeys(getTrustedBiddingSignalKeys(customAudience));
70 
71         if (shouldIncludeAds(customAudience)) {
72             customAudienceBuilder.addAllAdRenderIds(getAdRenderIds(customAudience));
73         }
74         return customAudienceBuilder.build();
75     }
76 
77     /**
78      * Builds a bidding and auction server protected signals proto from a {@link DBEncodedPayload}.
79      */
buildProtectedSignalsProtoFrom( DBEncodedPayload dbEncodedSignalsPayload)80     public BiddingAuctionServers.ProtectedAppSignals buildProtectedSignalsProtoFrom(
81             DBEncodedPayload dbEncodedSignalsPayload) {
82         BiddingAuctionServers.ProtectedAppSignals.Builder protectedSignalsBuilder =
83                 BiddingAuctionServers.ProtectedAppSignals.newBuilder();
84 
85         return protectedSignalsBuilder
86                 .setAppInstallSignals(
87                         ByteString.copyFrom(dbEncodedSignalsPayload.getEncodedPayload()))
88                 .setEncodingVersion(dbEncodedSignalsPayload.getVersion())
89                 .build();
90     }
91 
getUserBiddingSignals(DBCustomAudience customAudience)92     protected String getUserBiddingSignals(DBCustomAudience customAudience) {
93         return customAudience.getUserBiddingSignals() == null
94                 ? EMPTY_USER_BIDDING_SIGNALS
95                 : customAudience.getUserBiddingSignals().toString();
96     }
97 
getAdRenderIds(DBCustomAudience dbCustomAudience)98     private List<String> getAdRenderIds(DBCustomAudience dbCustomAudience) {
99         return dbCustomAudience.getAds().stream()
100                 .filter(ad -> !Strings.isNullOrEmpty(ad.getAdRenderId()))
101                 .map(ad -> ad.getAdRenderId())
102                 .collect(Collectors.toList());
103     }
104 
shouldIncludeAds(DBCustomAudience customAudience)105     private boolean shouldIncludeAds(DBCustomAudience customAudience) {
106         return !(mEnableOmitAds
107                 && ((customAudience.getAuctionServerRequestFlags()
108                                 & FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS)
109                         != 0));
110     }
111 
getTrustedBiddingSignalKeys(@onNull DBCustomAudience customAudience)112     protected List<String> getTrustedBiddingSignalKeys(@NonNull DBCustomAudience customAudience) {
113         List<String> biddingSignalKeys = customAudience.getTrustedBiddingData().getKeys();
114         // If the bidding signal keys is just the CA name, we don't need to pass it to the server.
115         if (biddingSignalKeys.size() == 1
116                 && customAudience.getName().equals(biddingSignalKeys.get(0))) {
117             return ImmutableList.of();
118         }
119 
120         // Remove the CA name from the bidding signal keys list to save space.
121         biddingSignalKeys.remove(customAudience.getName());
122         return biddingSignalKeys;
123     }
124 
calculateEncodedSignalsInBytes(byte[] encodedPayload)125     private int calculateEncodedSignalsInBytes(byte[] encodedPayload) {
126         return encodedPayload.length;
127     }
128 
129     /** Invokes {@link AuctionServerPayloadMetricsStrategy} to add per buyer intermediate stats. */
addToBuyerIntermediateStats( Map<AdTechIdentifier, BuyerInputGeneratorIntermediateStats> perBuyerStats, DBCustomAudience dbCustomAudience, BiddingAuctionServers.BuyerInput.CustomAudience customAudience)130     public void addToBuyerIntermediateStats(
131             Map<AdTechIdentifier, BuyerInputGeneratorIntermediateStats> perBuyerStats,
132             DBCustomAudience dbCustomAudience,
133             BiddingAuctionServers.BuyerInput.CustomAudience customAudience) {
134         mAuctionServerPayloadMetricsStrategy.addToBuyerIntermediateStats(
135                 perBuyerStats, dbCustomAudience, customAudience);
136     }
137 
138     /** Increments PAS extended metrics based on the length of the encoded payload. */
incrementPasExtendedMetrics(byte[] encodedPayload)139     public void incrementPasExtendedMetrics(byte[] encodedPayload) {
140         if (mPasExtendedMetricsEnabled) {
141             int encodedSignalsInBytes = calculateEncodedSignalsInBytes(encodedPayload);
142             mEncodedSignalsCount += 1;
143             mEncodedSignalsTotalSizeInBytes += encodedSignalsInBytes;
144             mEncodedSignalsMaxSizeInBytes =
145                     Math.max(mEncodedSignalsMaxSizeInBytes, encodedSignalsInBytes);
146             mEncodedSignalsMinSizeInBytes =
147                     Math.min(mEncodedSignalsMinSizeInBytes, encodedSignalsInBytes);
148         }
149     }
150 
151     /** Logs the buyer input generated stats. */
logBuyerInputGeneratedStats( Map<AdTechIdentifier, BuyerInputGeneratorIntermediateStats> perBuyerStats)152     public void logBuyerInputGeneratedStats(
153             Map<AdTechIdentifier, BuyerInputGeneratorIntermediateStats> perBuyerStats) {
154         // Log per buyer stats if feature is enabled
155         if (mPasExtendedMetricsEnabled) {
156             mAuctionServerPayloadMetricsStrategy
157                     .logGetAdSelectionDataBuyerInputGeneratedStatsWithExtendedPasMetrics(
158                             perBuyerStats,
159                             mEncodedSignalsCount,
160                             mEncodedSignalsTotalSizeInBytes,
161                             mEncodedSignalsMaxSizeInBytes,
162                             mEncodedSignalsMinSizeInBytes);
163         } else {
164             mAuctionServerPayloadMetricsStrategy.logGetAdSelectionDataBuyerInputGeneratedStats(
165                     perBuyerStats);
166         }
167     }
168 }
169