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.signature; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import android.adservices.adselection.AdWithBid; 22 import android.adservices.adselection.SignedContextualAds; 23 import android.adservices.common.AdData; 24 import android.adservices.common.AdFilters; 25 import android.adservices.common.AdTechIdentifier; 26 import android.adservices.common.AppInstallFilters; 27 import android.adservices.common.FrequencyCapFilters; 28 import android.adservices.common.KeyedFrequencyCap; 29 import android.net.Uri; 30 31 import com.android.adservices.shared.testing.SdkLevelSupportRule; 32 33 import com.google.common.collect.ImmutableList; 34 35 import org.junit.Before; 36 import org.junit.Rule; 37 import org.junit.Test; 38 39 import java.nio.charset.StandardCharsets; 40 import java.time.Duration; 41 import java.util.Arrays; 42 import java.util.Collections; 43 import java.util.Set; 44 import java.util.stream.Collectors; 45 46 public class BinarySerializerSignedContextualAdsTest { 47 public static final byte[] TEST_SIGNATURE = new byte[] {0, 1, 2}; 48 49 private SignedContextualAdsHashUtil mSerializer; 50 51 @Rule(order = 0) 52 public final SdkLevelSupportRule sdkLevel = SdkLevelSupportRule.forAtLeastS(); 53 54 @Before setUp()55 public void setUp() { 56 mSerializer = new SignedContextualAdsHashUtil(); 57 } 58 59 @Test testWriteString()60 public void testWriteString() { 61 String testString = "Hello"; 62 mSerializer.writeString(testString); 63 assertThat(bytesToString(mSerializer.getBytes())).isEqualTo(testString); 64 } 65 66 @Test testWriteLong()67 public void testWriteLong() { 68 long testLong = 123456789L; 69 mSerializer.writeLong(testLong); 70 assertThat(bytesToString(mSerializer.getBytes())).isEqualTo("" + testLong); 71 } 72 73 @Test testWriteDouble()74 public void testWriteDouble() { 75 double testDouble = 123.45; 76 mSerializer.writeDouble(testDouble); 77 assertThat(bytesToString(mSerializer.getBytes())).isEqualTo("" + testDouble); 78 } 79 80 @Test testWriteInt()81 public void testWriteInt() { 82 int testInt = 123; 83 mSerializer.writeInt(testInt); 84 assertThat(bytesToString(mSerializer.getBytes())).isEqualTo("" + testInt); 85 } 86 87 @Test testContextualAdSerialization_fullObject_success()88 public void testContextualAdSerialization_fullObject_success() { 89 String buyer = "https://example.com"; 90 String decisionLogicUri = "example.com/decisionLogic"; 91 String metadata = "metadata"; 92 String adCounterKeys = "1,2,3"; 93 String adRenderUri = "example.com/render"; 94 String bid = "5.10"; 95 int adCounterKey = 42; 96 int maxCount = 43; 97 Duration oneDay = Duration.ofDays(1); 98 KeyedFrequencyCap keyedFrequencyCap = 99 new KeyedFrequencyCap.Builder(adCounterKey, maxCount, oneDay).build(); 100 FrequencyCapFilters frequencyCapFilters = 101 new FrequencyCapFilters.Builder() 102 .setKeyedFrequencyCapsForClickEvents( 103 Collections.singletonList(keyedFrequencyCap)) 104 .setKeyedFrequencyCapsForWinEvents( 105 Collections.singletonList(keyedFrequencyCap)) 106 .setKeyedFrequencyCapsForImpressionEvents( 107 Collections.singletonList(keyedFrequencyCap)) 108 .setKeyedFrequencyCapsForViewEvents( 109 Collections.singletonList(keyedFrequencyCap)) 110 .build(); 111 Set<String> packageNames = Set.of("com.awesome.app1", "com.awesome.app2"); 112 AdFilters adFilters = 113 new AdFilters.Builder() 114 .setFrequencyCapFilters(frequencyCapFilters) 115 .setAppInstallFilters( 116 new AppInstallFilters.Builder() 117 .setPackageNames(packageNames) 118 .build()) 119 .build(); 120 String adRenderId = "987"; 121 122 Set<Integer> adCounterKeysSet = 123 Arrays.stream(adCounterKeys.split(",")) 124 .map(Integer::valueOf) 125 .collect(Collectors.toSet()); 126 SignedContextualAds contextualAds = 127 new SignedContextualAds.Builder() 128 .setBuyer(AdTechIdentifier.fromString(buyer)) 129 .setDecisionLogicUri(Uri.parse(decisionLogicUri)) 130 .setAdsWithBid( 131 ImmutableList.of( 132 new AdWithBid( 133 new AdData.Builder() 134 .setMetadata(metadata) 135 .setAdFilters(adFilters) 136 .setAdRenderId(adRenderId) 137 .setAdCounterKeys(adCounterKeysSet) 138 .setRenderUri(Uri.parse(adRenderUri)) 139 .build(), 140 Double.parseDouble(bid)))) 141 .setSignature(TEST_SIGNATURE) 142 .build(); 143 144 byte[] serialized = new SignedContextualAdsHashUtil().serialize(contextualAds); 145 146 /* 147 * This serialization is created manually following the rules explained in 148 * SignedContextualAds. Our serialization has to exactly match this to have consistency 149 * between ad tech and PPAPI. 150 */ 151 String expected = 152 String.format( 153 "buyer=%s|decision_logic_uri=%s|ads_with_bid=ad_data=ad_counter_keys=%s|" 154 + "ad_filters=app_install_filters=package_names=%s||" 155 + "frequency_cap_filters=keyed_frequency_caps_for_click_events=" 156 + "ad_counter_key=%s|interval=%s|max_count=%s||" 157 + "keyed_frequency_caps_for_impression_events=ad_counter_key=%s|" 158 + "interval=%s|max_count=%s||keyed_frequency_caps_for_view_events=" 159 + "ad_counter_key=%s|interval=%s|max_count=%s||" 160 + "keyed_frequency_caps_for_win_events=ad_counter_key=%s|" 161 + "interval=%s|max_count=%s||||ad_render_id=%s|metadata=%s|" 162 + "render_uri=%s||bid=%s||", 163 buyer, 164 decisionLogicUri, 165 adCounterKeys, 166 packageNames.stream().sorted().collect(Collectors.joining(",")), 167 adCounterKey, 168 oneDay.toMillis(), 169 maxCount, 170 adCounterKey, 171 oneDay.toMillis(), 172 maxCount, 173 adCounterKey, 174 oneDay.toMillis(), 175 maxCount, 176 adCounterKey, 177 oneDay.toMillis(), 178 maxCount, 179 adRenderId, 180 metadata, 181 adRenderUri, 182 bid); 183 assertThat(bytesToString(serialized)).isEqualTo(expected); 184 } 185 186 @Test testContextualAdSerialization_minimalistObject_success()187 public void testContextualAdSerialization_minimalistObject_success() { 188 String buyer = "https://example.com"; 189 String decisionLogicUri = "example.com/decisionLogic"; 190 String metadata = "metadata"; 191 String adCounterKeys = "1,2,3"; 192 String adRenderUri = "example.com/render"; 193 String bid = "5.10"; 194 SignedContextualAds contextualAds = 195 new SignedContextualAds.Builder() 196 .setBuyer(AdTechIdentifier.fromString(buyer)) 197 .setDecisionLogicUri(Uri.parse(decisionLogicUri)) 198 .setAdsWithBid( 199 ImmutableList.of( 200 new AdWithBid( 201 new AdData.Builder() 202 .setMetadata(metadata) 203 .setAdCounterKeys( 204 Arrays.stream( 205 adCounterKeys.split( 206 ",")) 207 .map(Integer::valueOf) 208 .collect( 209 Collectors.toSet())) 210 .setRenderUri(Uri.parse(adRenderUri)) 211 .build(), 212 Double.parseDouble(bid)))) 213 .setSignature(TEST_SIGNATURE) 214 .build(); 215 216 byte[] serialized = new SignedContextualAdsHashUtil().serialize(contextualAds); 217 218 String expected = 219 String.format( 220 "buyer=%s|decision_logic_uri=%s|ads_with_bid=ad_data=ad_counter_keys=%s|" 221 + "metadata=%s|render_uri=%s||bid=%s||", 222 buyer, decisionLogicUri, adCounterKeys, metadata, adRenderUri, bid); 223 assertThat(bytesToString(serialized)).isEqualTo(expected); 224 } 225 bytesToString(byte[] bytes)226 private String bytesToString(byte[] bytes) { 227 return new String(bytes, StandardCharsets.UTF_8); 228 } 229 } 230