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.data.common; 18 19 import android.adservices.common.AdSelectionSignals; 20 import android.adservices.common.AdTechIdentifier; 21 import android.net.Uri; 22 23 import androidx.annotation.Nullable; 24 import androidx.room.TypeConverter; 25 26 import com.android.adservices.LoggerFactory; 27 28 import org.json.JSONArray; 29 30 import java.time.Instant; 31 import java.util.HashSet; 32 import java.util.Optional; 33 import java.util.Set; 34 35 /** 36 * Room DB type converters for FLEDGE. 37 * 38 * <p>Register custom type converters here. 39 */ 40 public class FledgeRoomConverters { 41 private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); 42 FledgeRoomConverters()43 private FledgeRoomConverters() {} 44 45 /** Serialize {@link Instant} to Long. */ 46 @TypeConverter 47 @Nullable serializeInstant(@ullable Instant instant)48 public static Long serializeInstant(@Nullable Instant instant) { 49 return Optional.ofNullable(instant).map(Instant::toEpochMilli).orElse(null); 50 } 51 52 /** Deserialize {@link Instant} from long. */ 53 @TypeConverter 54 @Nullable deserializeInstant(@ullable Long epochMilli)55 public static Instant deserializeInstant(@Nullable Long epochMilli) { 56 return Optional.ofNullable(epochMilli).map(Instant::ofEpochMilli).orElse(null); 57 } 58 59 /** Deserialize {@link Uri} from String. */ 60 @TypeConverter 61 @Nullable deserializeUri(@ullable String uri)62 public static Uri deserializeUri(@Nullable String uri) { 63 return Optional.ofNullable(uri).map(Uri::parse).orElse(null); 64 } 65 66 /** Serialize {@link Uri} to String. */ 67 @TypeConverter 68 @Nullable serializeUri(@ullable Uri uri)69 public static String serializeUri(@Nullable Uri uri) { 70 return Optional.ofNullable(uri).map(Uri::toString).orElse(null); 71 } 72 73 /** Serialize an {@link AdTechIdentifier} to String. */ 74 @TypeConverter 75 @Nullable serializeAdTechIdentifier(@ullable AdTechIdentifier adTechIdentifier)76 public static String serializeAdTechIdentifier(@Nullable AdTechIdentifier adTechIdentifier) { 77 return Optional.ofNullable(adTechIdentifier).map(AdTechIdentifier::toString).orElse(null); 78 } 79 80 /** Deserialize an {@link AdTechIdentifier} from a String. */ 81 @TypeConverter 82 @Nullable deserializeAdTechIdentifier(@ullable String adTechIdentifier)83 public static AdTechIdentifier deserializeAdTechIdentifier(@Nullable String adTechIdentifier) { 84 return Optional.ofNullable(adTechIdentifier).map(AdTechIdentifier::fromString).orElse(null); 85 } 86 87 /** Serialize an {@link AdSelectionSignals} to String. */ 88 @TypeConverter 89 @Nullable serializeAdSelectionSignals(@ullable AdSelectionSignals signals)90 public static String serializeAdSelectionSignals(@Nullable AdSelectionSignals signals) { 91 return Optional.ofNullable(signals).map(AdSelectionSignals::toString).orElse(null); 92 } 93 94 /** Deserialize an {@link AdSelectionSignals} from a String. */ 95 @TypeConverter 96 @Nullable deserializeAdSelectionSignals(@ullable String signals)97 public static AdSelectionSignals deserializeAdSelectionSignals(@Nullable String signals) { 98 return Optional.ofNullable(signals).map(AdSelectionSignals::fromString).orElse(null); 99 } 100 101 /** Serialize a {@link Set} of Strings into a JSON array as a String. */ 102 @TypeConverter 103 @Nullable serializeStringSet(@ullable Set<String> stringSet)104 public static String serializeStringSet(@Nullable Set<String> stringSet) { 105 if (stringSet == null) { 106 return null; 107 } 108 109 JSONArray jsonSet = new JSONArray(stringSet); 110 return jsonSet.toString(); 111 } 112 113 /** Deserialize a {@link Set} of Strings from a JSON array. */ 114 @TypeConverter 115 @Nullable deserializeStringSet(@ullable String serializedSet)116 public static Set<String> deserializeStringSet(@Nullable String serializedSet) { 117 if (serializedSet == null) { 118 return null; 119 } 120 121 Set<String> outputSet = new HashSet<>(); 122 JSONArray jsonSet; 123 try { 124 jsonSet = new JSONArray(serializedSet); 125 } catch (Exception exception) { 126 sLogger.d(exception, "Error deserializing set of strings from DB; returning null set"); 127 return null; 128 } 129 130 for (int arrayIndex = 0; arrayIndex < jsonSet.length(); arrayIndex++) { 131 String currentString; 132 try { 133 currentString = jsonSet.getString(arrayIndex); 134 } catch (Exception exception) { 135 // getString() coerces elements into Strings, so this should only happen if we get 136 // out of bounds 137 sLogger.d( 138 exception, 139 "Error deserializing set string #%d from DB; skipping any other elements", 140 arrayIndex); 141 break; 142 } 143 outputSet.add(currentString); 144 } 145 146 return outputSet; 147 } 148 149 /** Serialize a {@link Set} of Integers into a JSON array as a String. */ 150 @TypeConverter 151 @Nullable serializeIntegerSet(@ullable Set<Integer> integerSet)152 public static String serializeIntegerSet(@Nullable Set<Integer> integerSet) { 153 if (integerSet == null) { 154 return null; 155 } 156 157 JSONArray jsonSet = new JSONArray(integerSet); 158 return jsonSet.toString(); 159 } 160 161 /** Deserialize a {@link Set} of Strings from a JSON array. */ 162 @TypeConverter 163 @Nullable deserializeIntegerSet(@ullable String serializedSet)164 public static Set<Integer> deserializeIntegerSet(@Nullable String serializedSet) { 165 if (serializedSet == null) { 166 return null; 167 } 168 169 Set<Integer> outputSet = new HashSet<>(); 170 JSONArray jsonSet; 171 try { 172 jsonSet = new JSONArray(serializedSet); 173 } catch (Exception exception) { 174 sLogger.d(exception, "Error deserializing set of ints from DB; returning null set"); 175 return null; 176 } 177 178 for (int arrayIndex = 0; arrayIndex < jsonSet.length(); arrayIndex++) { 179 int currentInt; 180 try { 181 currentInt = jsonSet.getInt(arrayIndex); 182 } catch (Exception exception) { 183 sLogger.d( 184 exception, 185 "Error deserializing set int #%d from DB; skipping element from %s", 186 arrayIndex, 187 serializedSet); 188 continue; 189 } 190 outputSet.add(currentInt); 191 } 192 193 return outputSet; 194 } 195 } 196