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.kanon; 18 19 import android.annotation.NonNull; 20 21 import com.android.adservices.data.kanon.DBKAnonMessage; 22 import com.android.adservices.data.kanon.KAnonMessageConstants; 23 import com.android.adservices.data.kanon.KAnonMessageDao; 24 import com.android.adservices.service.Flags; 25 26 import java.time.Clock; 27 import java.util.ArrayList; 28 import java.util.List; 29 import java.util.Objects; 30 import java.util.stream.Collectors; 31 32 /** Class to manage fetching and persisting of {@link KAnonMessageEntity}. */ 33 public class KAnonMessageManager { 34 35 @NonNull private final KAnonMessageDao mKAnonMessageDao; 36 @NonNull private final Flags mFlags; 37 @NonNull private final Clock mClock; 38 KAnonMessageManager( @onNull KAnonMessageDao kAnonMessageDao, @NonNull Flags flags, @NonNull Clock clock)39 public KAnonMessageManager( 40 @NonNull KAnonMessageDao kAnonMessageDao, @NonNull Flags flags, @NonNull Clock clock) { 41 Objects.requireNonNull(kAnonMessageDao); 42 Objects.requireNonNull(flags); 43 Objects.requireNonNull(clock); 44 45 mKAnonMessageDao = kAnonMessageDao; 46 mFlags = flags; 47 mClock = clock; 48 } 49 50 /** 51 * This method is used to persist NEW {@link KAnonMessageEntity} in the {@link DBKAnonMessage} 52 * table. 53 */ persistNewAnonMessageEntities( List<KAnonMessageEntity> kAnonMessageEntityList)54 public List<KAnonMessageEntity> persistNewAnonMessageEntities( 55 List<KAnonMessageEntity> kAnonMessageEntityList) { 56 List<DBKAnonMessage> dbkAnonMessages = 57 kAnonMessageEntityList.stream() 58 .map(this::parseKAnonMessageEntityToNewDBKAnonMessage) 59 .collect(Collectors.toList()); 60 long[] ids = mKAnonMessageDao.insertAllKAnonMessages(dbkAnonMessages); 61 List<KAnonMessageEntity> newKAnonMessageEntities = new ArrayList<>(); 62 for (int i = 0; i < ids.length; i++) { 63 KAnonMessageEntity currentEntity = kAnonMessageEntityList.get(i); 64 newKAnonMessageEntities.add( 65 KAnonMessageEntity.builder() 66 .setAdSelectionId(currentEntity.getAdSelectionId()) 67 .setMessageId(ids[i]) 68 .setHashSet(currentEntity.getHashSet()) 69 .setStatus(currentEntity.getStatus()) 70 .build()); 71 } 72 return newKAnonMessageEntities; 73 } 74 75 /** 76 * This method fetches a list of {@link DBKAnonMessage} from the database, parses it and returns 77 * a list of {@link KAnonMessageEntity}. 78 * 79 * @param numberOfMessages number of messages to be fetched. 80 * @param status status of the messages that needed to be fetched 81 */ fetchNKAnonMessagesWithStatus( int numberOfMessages, @KAnonMessageConstants.MessageStatus int status)82 public List<KAnonMessageEntity> fetchNKAnonMessagesWithStatus( 83 int numberOfMessages, @KAnonMessageConstants.MessageStatus int status) { 84 return mKAnonMessageDao.getNLatestKAnonMessagesWithStatus(numberOfMessages, status).stream() 85 .map(this::parseDBKAnonMessageToKAnonMessageEntity) 86 .collect(Collectors.toList()); 87 } 88 89 /** 90 * @param hashSetToSearch Fetches the {@link DBKAnonMessage} matching the searchText and parses 91 * it to a {@link KAnonMessageEntity} 92 */ fetchKAnonMessageEntityWithMessage(String hashSetToSearch)93 public List<KAnonMessageEntity> fetchKAnonMessageEntityWithMessage(String hashSetToSearch) { 94 return mKAnonMessageDao.getKAnonMessagesWithMessage(hashSetToSearch).stream() 95 .map(this::parseDBKAnonMessageToKAnonMessageEntity) 96 .collect(Collectors.toList()); 97 } 98 99 /** 100 * Returns the number of messages with NOT_PROCESSED {@link KAnonMessageConstants.MessageStatus} 101 * status in the database. 102 */ getNumberOfUnprocessedMessagesInDB()103 public int getNumberOfUnprocessedMessagesInDB() { 104 return mKAnonMessageDao.getNumberOfMessagesWithStatus( 105 KAnonMessageConstants.MessageStatus.NOT_PROCESSED); 106 } 107 108 /** Updates the status of messages in the table. */ updateMessagesStatus( List<KAnonMessageEntity> messageEntities, @KAnonMessageEntity.KanonMessageEntityStatus int status)109 public void updateMessagesStatus( 110 List<KAnonMessageEntity> messageEntities, 111 @KAnonMessageEntity.KanonMessageEntityStatus int status) { 112 List<Long> idsToUpdate = 113 messageEntities.stream() 114 .map(KAnonMessageEntity::getMessageId) 115 .collect(Collectors.toList()); 116 117 mKAnonMessageDao.updateMessagesStatus( 118 idsToUpdate, KAnonMessageConstants.fromKAnonMessageEntityStatus(status)); 119 } 120 parseKAnonMessageEntityToNewDBKAnonMessage( KAnonMessageEntity kAnonMessageEntity)121 private DBKAnonMessage parseKAnonMessageEntityToNewDBKAnonMessage( 122 KAnonMessageEntity kAnonMessageEntity) { 123 if (kAnonMessageEntity == null) { 124 return null; 125 } 126 return DBKAnonMessage.builder() 127 .setAdSelectionId(kAnonMessageEntity.getAdSelectionId()) 128 .setKanonHashSet(kAnonMessageEntity.getHashSet()) 129 .setStatus( 130 KAnonMessageConstants.fromKAnonMessageEntityStatus( 131 kAnonMessageEntity.getStatus())) 132 // TODO(b/325606196): stable kanon flags. 133 .setExpiryInstant( 134 mClock.instant().plusSeconds(mFlags.getFledgeKAnonMessageTtlSeconds())) 135 .setCreatedAt(mClock.instant()) 136 .build(); 137 } 138 parseDBKAnonMessageToKAnonMessageEntity( DBKAnonMessage dbkAnonMessage)139 private KAnonMessageEntity parseDBKAnonMessageToKAnonMessageEntity( 140 DBKAnonMessage dbkAnonMessage) { 141 if (dbkAnonMessage == null) { 142 return null; 143 } 144 return KAnonMessageEntity.builder() 145 .setMessageId(dbkAnonMessage.getMessageId()) 146 .setHashSet(dbkAnonMessage.getKanonHashSet()) 147 .setAdSelectionId(dbkAnonMessage.getAdSelectionId()) 148 .setStatus( 149 KAnonMessageConstants.toKAnonMessageEntityStatus( 150 dbkAnonMessage.getStatus())) 151 .setCorrespondingClientParametersExpiryInstant( 152 dbkAnonMessage.getCorrespondingClientParametersExpiryInstant()) 153 .build(); 154 } 155 } 156