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