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. */ create( ReportKey reportKey, int dayIndex, EventVector eventVector, SystemProfile systemProfile, AggregateValue aggregateValue)78 public static AggregateStoreTableRow create( 79 ReportKey reportKey, 80 int dayIndex, 81 EventVector eventVector, 82 SystemProfile systemProfile, 83 AggregateValue aggregateValue) { 84 return builder() 85 .setReportKey(reportKey) 86 .setDayIndex(dayIndex) 87 .setEventVector(eventVector) 88 .setSystemProfile(systemProfile) 89 .setAggregateValue(aggregateValue) 90 .build(); 91 } 92 93 /** Get the report key of the row. */ 94 @CopyAnnotations 95 @Embedded 96 @NonNull reportKey()97 public abstract ReportKey reportKey(); 98 99 /** Get the event vector of the row. */ 100 @CopyAnnotations 101 @ColumnInfo(name = "event_vector") 102 @NonNull eventVector()103 public abstract EventVector eventVector(); 104 105 /** Get the system profile of the row. */ 106 @CopyAnnotations 107 @ColumnInfo(name = "system_profile") 108 @NonNull systemProfile()109 public abstract SystemProfile systemProfile(); 110 111 /** Get the day index of the row. */ 112 @CopyAnnotations 113 @ColumnInfo(name = "day_index") dayIndex()114 public abstract int dayIndex(); 115 116 /** Get the aggregate value of the row. */ 117 @CopyAnnotations 118 @ColumnInfo(name = "aggregate_value") 119 @NonNull aggregateValue()120 public abstract AggregateValue aggregateValue(); 121 } 122 123 /** Get all the aggregate data from the database. */ 124 @Query( 125 "SELECT customer_id, project_id, metric_id, report_id, day_index, event_vector, " 126 + "system_profile, aggregate_value FROM AggregateStore INNER JOIN " 127 + "SystemProfiles ON AggregateStore.system_profile_hash = SystemProfiles" 128 + ".system_profile_hash " 129 + "ORDER BY customer_id, project_id, metric_id, report_id, day_index, " 130 + "event_vector, AggregateStore.system_profile_hash, aggregate_value") getAllAggregates()131 public abstract List<AggregateStoreTableRow> getAllAggregates(); 132 133 /** Insert and aggregate value row. */ insertAggregateValue(AggregateStoreTableRow aggregateStoreTableRow)134 public void insertAggregateValue(AggregateStoreTableRow aggregateStoreTableRow) { 135 long systemProfileHash = 136 SystemProfileEntity.getSystemProfileHash(aggregateStoreTableRow.systemProfile()); 137 insertLastSentDayIndex(ReportEntity.create(aggregateStoreTableRow.reportKey())); 138 insertSystemProfile( 139 SystemProfileEntity.create( 140 systemProfileHash, aggregateStoreTableRow.systemProfile())); 141 insertAggregateValue( 142 AggregateStoreEntity.create( 143 aggregateStoreTableRow.reportKey(), 144 aggregateStoreTableRow.dayIndex(), 145 aggregateStoreTableRow.eventVector(), 146 systemProfileHash, 147 aggregateStoreTableRow.aggregateValue())); 148 } 149 150 /** 151 * Insert the day a report was last sent. 152 * 153 * @param reportKey the report 154 * @param dayIndex the day 155 */ 156 @Insert(onConflict = OnConflictStrategy.IGNORE) insertLastSentDayIndex(ReportKey reportKey, int dayIndex)157 public Void insertLastSentDayIndex(ReportKey reportKey, int dayIndex) { 158 return insertLastSentDayIndex(ReportEntity.create(reportKey, dayIndex)); 159 } 160 161 @Insert(onConflict = OnConflictStrategy.IGNORE) insertLastSentDayIndex(ReportEntity reportEntity)162 abstract Void insertLastSentDayIndex(ReportEntity reportEntity); 163 164 @Insert(onConflict = OnConflictStrategy.IGNORE) insertSystemProfile(SystemProfileEntity systemProfileEntity)165 abstract Void insertSystemProfile(SystemProfileEntity systemProfileEntity); 166 167 @Insert(onConflict = OnConflictStrategy.ROLLBACK) insertAggregateValue(AggregateStoreEntity aggregateStoreEntity)168 abstract Void insertAggregateValue(AggregateStoreEntity aggregateStoreEntity); 169 170 /** Get the time Cobalt was enabled. */ getInitialEnabledTime()171 public Optional<Instant> getInitialEnabledTime() { 172 return Optional.ofNullable( 173 queryEnablementTimes().get(GlobalValueEntity.Key.INITIAL_ENABLED_TIME)) 174 .map(GlobalValueEntity::timeFromDbString); 175 } 176 177 /** Get the time Cobalt was disabled. */ getStartDisabledTime()178 public Optional<Instant> getStartDisabledTime() { 179 return Optional.ofNullable( 180 queryEnablementTimes().get(GlobalValueEntity.Key.INITIAL_DISABLED_TIME)) 181 .map(GlobalValueEntity::timeFromDbString); 182 } 183 184 @MapInfo(keyColumn = "key", valueColumn = "value") 185 @Query( 186 "SELECT * FROM GlobalValues WHERE key IN ('INITIAL_ENABLED_TIME'," 187 + " 'INITIAL_DISABLED_TIME')") queryEnablementTimes()188 abstract Map<GlobalValueEntity.Key, String> queryEnablementTimes(); 189 190 /** 191 * Return the day a report was last sent, if in the reports table. 192 * 193 * @param reportKey the report 194 * @return the last sent day index, if found 195 */ queryLastSentDayIndex(ReportKey reportKey)196 public Optional<Integer> queryLastSentDayIndex(ReportKey reportKey) { 197 return queryLastSentDayIndex( 198 reportKey.customerId(), 199 reportKey.projectId(), 200 reportKey.metricId(), 201 reportKey.reportId()); 202 } 203 204 /** 205 * Return the day a report was last sent, if in the reports table. 206 * 207 * @param customerId the customer id for the report 208 * @param projectId the project id for the report 209 * @param metricId the metric id for the report 210 * @param reportId the report id for the report 211 * @return the last sent day index, if found 212 */ 213 @Query( 214 "SELECT last_sent_day_index " 215 + "FROM Reports " 216 + "WHERE customer_id = :customerId " 217 + "AND project_id = :projectId " 218 + "AND metric_id = :metricId " 219 + "AND report_id = :reportId") queryLastSentDayIndex( long customerId, long projectId, long metricId, long reportId)220 abstract Optional<Integer> queryLastSentDayIndex( 221 long customerId, long projectId, long metricId, long reportId); 222 223 /** Delete all reports from the report store. */ 224 @VisibleForTesting 225 @Query("DELETE FROM Reports") deleteAllReports()226 public abstract void deleteAllReports(); 227 228 /** Get all the repory keys in the report store. */ 229 @VisibleForTesting 230 @Query("SELECT customer_id, project_id, metric_id, report_id FROM Reports") getReportKeys()231 public abstract List<ReportKey> getReportKeys(); 232 233 /** Get all the unencrypted observation batches in the observation store. */ 234 @VisibleForTesting 235 @Query("SELECT unencrypted_observation_batch FROM ObservationStore") getObservationBatches()236 public abstract List<UnencryptedObservationBatch> getObservationBatches(); 237 238 /** Get all the report ids in the aggregate store. */ 239 @VisibleForTesting 240 @Query("SELECT report_id from AggregateStore") getAggregatedReportIds()241 public abstract List<Integer> getAggregatedReportIds(); 242 243 /** Get all the day indices in the aggregate store. */ 244 @VisibleForTesting 245 @Query("SELECT day_index from AggregateStore") getDayIndices()246 public abstract List<Integer> getDayIndices(); 247 248 /** Get all string hashes in the string hash store. */ 249 @VisibleForTesting 250 @Query("SELECT * FROM StringHashes") getStringHashes()251 public abstract List<StringHashEntity> getStringHashes(); 252 253 /** Gets all system profiles from the system profile table. */ 254 @VisibleForTesting 255 @Query("SELECT * FROM SystemProfiles") getSystemProfiles()256 public abstract List<SystemProfileEntity> getSystemProfiles(); 257 } 258