1 /*
2  * Copyright (C) 2023 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 android.annotation.NonNull;
20 import android.net.Uri;
21 
22 import com.android.adservices.LogUtil;
23 import com.android.adservices.data.customaudience.DBTrustedBiddingData;
24 import com.android.internal.annotations.VisibleForTesting;
25 
26 
27 import org.json.JSONException;
28 import org.json.JSONObject;
29 
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Objects;
33 
34 /** Utility class to fetch the data version from trusted bidding/scoring signals. */
35 public class DataVersionFetcher {
36     public static final String DATA_VERSION_HEADER_BIDDING_KEY =
37             "x-fledge-bidding-signals-format-version";
38 
39     public static final String DATA_VERSION_HEADER_SCORING_KEY =
40             "x-fledge-scoring-signals-format-version";
41 
42     @VisibleForTesting static final int MAX_UNSIGNED_8_BIT = 255;
43 
44     /**
45      * Extracts the data version for the buyer and returns it if it exists.
46      *
47      * @param trustedBiddingData contains the {@link Uri} which is used as a key to fetch the
48      *     headers for this ad.
49      * @param trustedBiddingResponsesByBaseUri contains the trusted bidding headers for this ad
50      *     where we may find the data version header.
51      * @throws IllegalStateException if the data version does not exist for this combination of
52      *     {@link DBTrustedBiddingData#getUri()} and {@code Map<Uri, TrustedBiddingResponse>
53      *     trustedBiddingResponsesByBaseUri}, or if {@link TrustedBiddingResponse#getHeaders()} does
54      *     not conform to the expected requirements.
55      */
getBuyerDataVersion( @onNull DBTrustedBiddingData trustedBiddingData, @NonNull Map<Uri, TrustedBiddingResponse> trustedBiddingResponsesByBaseUri)56     public static int getBuyerDataVersion(
57             @NonNull DBTrustedBiddingData trustedBiddingData,
58             @NonNull Map<Uri, TrustedBiddingResponse> trustedBiddingResponsesByBaseUri)
59             throws IllegalStateException {
60         if (Objects.isNull(trustedBiddingData)
61                 || Objects.isNull(
62                         trustedBiddingResponsesByBaseUri.get(trustedBiddingData.getUri()))) {
63             throw new IllegalStateException("Data Version Header does not exist");
64         }
65         int dataVersion;
66         JSONObject headers;
67         try {
68             headers =
69                     trustedBiddingResponsesByBaseUri.get(trustedBiddingData.getUri()).getHeaders();
70             dataVersion = headers.getJSONArray(DATA_VERSION_HEADER_BIDDING_KEY).getInt(0);
71         } catch (JSONException e) {
72             LogUtil.v("Data version header does not conform required header formatting.");
73             throw new IllegalStateException(
74                     "Data version header does not conform required header formatting.");
75         }
76         if (dataVersion < 0 || dataVersion > MAX_UNSIGNED_8_BIT) {
77             LogUtil.v("Header object: " + headers + " does not conform to 8 bit requirements.");
78             throw new IllegalStateException(
79                     "Data version header does not conform to unsigned 8 bit requirements,");
80         }
81         return dataVersion;
82     }
83 
84     /**
85      * Extracts the data version for the seller and returns it if it exists.
86      *
87      * @param headers contains the trusted scoring headers for this seller where we may find the
88      *     data version header.
89      * @throws IllegalStateException if the data version does not exist or it does not conform to
90      *     the expected requirements.
91      */
getSellerDataVersion(@onNull Map<String, List<String>> headers)92     public static int getSellerDataVersion(@NonNull Map<String, List<String>> headers)
93             throws IllegalStateException {
94         if (Objects.isNull(headers)) {
95             throw new IllegalStateException("Data Version Header does not exist");
96         }
97         int dataVersion;
98         try {
99             dataVersion = Integer.parseInt(headers.get(DATA_VERSION_HEADER_SCORING_KEY).get(0));
100         } catch (Exception e) {
101             LogUtil.v("Data version header does not conform required header formatting.");
102             throw new IllegalStateException(
103                     "Data version header does not conform required header formatting.");
104         }
105         if (dataVersion < 0 || dataVersion > MAX_UNSIGNED_8_BIT) {
106             LogUtil.v("Data version: " + dataVersion + " does not conform to 8 bit requirements.");
107             throw new IllegalStateException(
108                     "Data version header does not conform to unsigned 8 bit requirements,");
109         }
110         return dataVersion;
111     }
112 }
113