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.cobalt.data; 18 19 import androidx.annotation.NonNull; 20 import androidx.room.ColumnInfo; 21 import androidx.room.Dao; 22 import androidx.room.Embedded; 23 import androidx.room.Insert; 24 import androidx.room.MapInfo; 25 import androidx.room.OnConflictStrategy; 26 import androidx.room.Query; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 30 import com.google.auto.value.AutoValue; 31 import com.google.auto.value.AutoValue.CopyAnnotations; 32 import com.google.cobalt.AggregateValue; 33 import com.google.cobalt.SystemProfile; 34 import com.google.cobalt.UnencryptedObservationBatch; 35 36 import java.time.Instant; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Optional; 40 41 /** Data Access Object offering database operations only required in tests. */ 42 @Dao 43 @VisibleForTesting 44 public abstract class TestOnlyDao { 45 46 /** Helper class for retrieving rows of the AggregateStore table. */ 47 @AutoValue 48 @CopyAnnotations 49 public abstract static class AggregateStoreTableRow { 50 /** Get a builder for the row. */ builder()51 public static Builder builder() { 52 return new AutoValue_TestOnlyDao_AggregateStoreTableRow.Builder(); 53 } 54 55 /** Builder class for creating an AggregateStoreTableRow. */ 56 @AutoValue.Builder 57 public abstract static class Builder { 58 /** Set the report key for the row. */ setReportKey(ReportKey reportKey)59 public abstract Builder setReportKey(ReportKey reportKey); 60 61 /** Set the event vector for the row. */ setEventVector(EventVector value)62 public abstract Builder setEventVector(EventVector value); 63 64 /** Set the system profile for the row. */ setSystemProfile(SystemProfile value)65 public abstract Builder setSystemProfile(SystemProfile value); 66 67 /** Set the day index for the row. */ setDayIndex(int value)68 public abstract Builder setDayIndex(int value); 69 70 /** Set the aggregate value for the row. */ setAggregateValue(AggregateValue value)71 public abstract Builder setAggregateValue(AggregateValue value); 72 73 /** Build the row. */ build()74 public abstract AggregateStoreTableRow build(); 75 } 76 77 /** Create a new row. */ 78 @NonNull create( ReportKey reportKey, int dayIndex, EventVector eventVector, SystemProfile systemProfile, AggregateValue aggregateValue)79 public static AggregateStoreTableRow create( 80 ReportKey reportKey, 81 int dayIndex, 82 EventVector eventVector, 83 SystemProfile systemProfile, 84 AggregateValue aggregateValue) { 85 return builder() 86 .setReportKey(reportKey) 87 .setDayIndex(dayIndex) 88 .setEventVector(eventVector) 89 .setSystemProfile(systemProfile) 90 .setAggregateValue(aggregateValue) 91 .build(); 92 } 93 94 /** Get the report key of the row. */ 95 @CopyAnnotations 96 @Embedded 97 @NonNull reportKey()98 public abstract ReportKey reportKey(); 99 100 /** Get the event vector of the row. */ 101 @CopyAnnotations 102 @ColumnInfo(name = "event_vector") 103 @NonNull eventVector()104 public abstract EventVector eventVector(); 105 106 /** Get the system profile of the row. */ 107 @CopyAnnotations 108 @ColumnInfo(name = "system_profile") 109 @NonNull systemProfile()110 public abstract SystemProfile systemProfile(); 111 112 /** Get the day index of the row. */ 113 @CopyAnnotations 114 @ColumnInfo(name = "day_index") dayIndex()115 public abstract int dayIndex(); 116 117 /** Get the aggregate value of the row. */ 118 @CopyAnnotations 119 @ColumnInfo(name = "aggregate_value") 120 @NonNull aggregateValue()121 public abstract AggregateValue aggregateValue(); 122 } 123 124 /** Get all the aggregate data from the database. */ 125 @Query( 126 "SELECT customer_id, project_id, metric_id, report_id, day_index, event_vector, " 127 + "system_profile, aggregate_value FROM AggregateStore INNER JOIN " 128 + "SystemProfiles ON AggregateStore.system_profile_hash = SystemProfiles" 129 + ".system_profile_hash " 130 + "ORDER BY customer_id, project_id, metric_id, report_id, day_index, " 131 + "event_vector, AggregateStore.system_profile_hash, aggregate_value") getAllAggregates()132 public abstract List<AggregateStoreTableRow> getAllAggregates(); 133 134 /** Insert and aggregate value row. */ insertAggregateValue(AggregateStoreTableRow aggregateStoreTableRow)135 public void insertAggregateValue(AggregateStoreTableRow aggregateStoreTableRow) { 136 long systemProfileHash = 137 SystemProfileEntity.getSystemProfileHash(aggregateStoreTableRow.systemProfile()); 138 insertLastSentDayIndex(ReportEntity.create(aggregateStoreTableRow.reportKey())); 139 insertSystemProfile( 140 SystemProfileEntity.create( 141 systemProfileHash, aggregateStoreTableRow.systemProfile())); 142 insertAggregateValue( 143 AggregateStoreEntity.create( 144 aggregateStoreTableRow.reportKey(), 145 aggregateStoreTableRow.dayIndex(), 146 aggregateStoreTableRow.eventVector(), 147 systemProfileHash, 148 aggregateStoreTableRow.aggregateValue())); 149 } 150 151 /** 152 * Insert the day a report was last sent. 153 * 154 * @param reportKey the report 155 * @param dayIndex the day 156 */ 157 @Insert(onConflict = OnConflictStrategy.IGNORE) insertLastSentDayIndex(ReportKey reportKey, int dayIndex)158 public Void insertLastSentDayIndex(ReportKey reportKey, int dayIndex) { 159 return insertLastSentDayIndex(ReportEntity.create(reportKey, dayIndex)); 160 } 161 162 @Insert(onConflict = OnConflictStrategy.IGNORE) insertLastSentDayIndex(ReportEntity reportEntity)163 abstract Void insertLastSentDayIndex(ReportEntity reportEntity); 164 165 @Insert(onConflict = OnConflictStrategy.IGNORE) insertSystemProfile(SystemProfileEntity systemProfileEntity)166 abstract Void insertSystemProfile(SystemProfileEntity systemProfileEntity); 167 168 @Insert(onConflict = OnConflictStrategy.ROLLBACK) insertAggregateValue(AggregateStoreEntity aggregateStoreEntity)169 abstract Void insertAggregateValue(AggregateStoreEntity aggregateStoreEntity); 170 171 /** Get the time Cobalt was enabled. */ getInitialEnabledTime()172 public Optional<Instant> getInitialEnabledTime() { 173 return Optional.ofNullable( 174 queryEnablementTimes().get(GlobalValueEntity.Key.INITIAL_ENABLED_TIME)) 175 .map(GlobalValueEntity::timeFromDbString); 176 } 177 178 /** Get the time Cobalt was disabled. */ getStartDisabledTime()179 public Optional<Instant> getStartDisabledTime() { 180 return Optional.ofNullable( 181 queryEnablementTimes().get(GlobalValueEntity.Key.INITIAL_DISABLED_TIME)) 182 .map(GlobalValueEntity::timeFromDbString); 183 } 184 185 @MapInfo(keyColumn = "key", valueColumn = "value") 186 @Query( 187 "SELECT * FROM GlobalValues WHERE key IN ('INITIAL_ENABLED_TIME'," 188 + " 'INITIAL_DISABLED_TIME')") queryEnablementTimes()189 abstract Map<GlobalValueEntity.Key, String> queryEnablementTimes(); 190 191 /** 192 * Return the day a report was last sent, if in the reports table. 193 * 194 * @param reportKey the report 195 * @return the last sent day index, if found 196 */ queryLastSentDayIndex(ReportKey reportKey)197 public Optional<Integer> queryLastSentDayIndex(ReportKey reportKey) { 198 return queryLastSentDayIndex( 199 reportKey.customerId(), 200 reportKey.projectId(), 201 reportKey.metricId(), 202 reportKey.reportId()); 203 } 204 205 /** 206 * Return the day a report was last sent, if in the reports table. 207 * 208 * @param customerId the customer id for the report 209 * @param projectId the project id for the report 210 * @param metricId the metric id for the report 211 * @param reportId the report id for the report 212 * @return the last sent day index, if found 213 */ 214 @Query( 215 "SELECT last_sent_day_index " 216 + "FROM Reports " 217 + "WHERE customer_id = :customerId " 218 + "AND project_id = :projectId " 219 + "AND metric_id = :metricId " 220 + "AND report_id = :reportId") queryLastSentDayIndex( long customerId, long projectId, long metricId, long reportId)221 abstract Optional<Integer> queryLastSentDayIndex( 222 long customerId, long projectId, long metricId, long reportId); 223 224 /** Delete all reports from the report store. */ 225 @VisibleForTesting 226 @Query("DELETE FROM Reports") deleteAllReports()227 public abstract void deleteAllReports(); 228 229 /** Get all the repory keys in the report store. */ 230 @VisibleForTesting 231 @Query("SELECT customer_id, project_id, metric_id, report_id FROM Reports") getReportKeys()232 public abstract List<ReportKey> getReportKeys(); 233 234 /** Get all the unencrypted observation batches in the observation store. */ 235 @VisibleForTesting 236 @Query("SELECT unencrypted_observation_batch FROM ObservationStore") getObservationBatches()237 public abstract List<UnencryptedObservationBatch> getObservationBatches(); 238 239 /** Get all the report ids in the aggregate store. */ 240 @VisibleForTesting 241 @Query("SELECT report_id from AggregateStore") getAggregatedReportIds()242 public abstract List<Integer> getAggregatedReportIds(); 243 244 /** Get all the day indices in the aggregate store. */ 245 @VisibleForTesting 246 @Query("SELECT day_index from AggregateStore") getDayIndices()247 public abstract List<Integer> getDayIndices(); 248 249 /** Get all string hashes in the string hash store. */ 250 @VisibleForTesting 251 @Query("SELECT * FROM StringHashes") getStringHashes()252 public abstract List<StringHashEntity> getStringHashes(); 253 254 /** Gets all system profiles from the system profile table. */ 255 @VisibleForTesting 256 @Query("SELECT * FROM SystemProfiles") getSystemProfiles()257 public abstract List<SystemProfileEntity> getSystemProfiles(); 258 } 259