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.Entity;
22 import androidx.room.Ignore;
23 import androidx.room.PrimaryKey;
24 
25 import com.android.cobalt.crypto.Encrypter;
26 import com.android.cobalt.crypto.EncryptionFailedException;
27 
28 import com.google.auto.value.AutoValue;
29 import com.google.auto.value.AutoValue.CopyAnnotations;
30 import com.google.cobalt.EncryptedMessage;
31 import com.google.cobalt.ObservationBatch;
32 import com.google.cobalt.ObservationToEncrypt;
33 import com.google.cobalt.UnencryptedObservationBatch;
34 
35 import java.util.Objects;
36 import java.util.Optional;
37 
38 /**
39  * Stores observations which have been generated, but not sent.
40  *
41  * <p>Observations are automatically assigned a montonically increasing id.
42  */
43 @AutoValue
44 @CopyAnnotations
45 @Entity(tableName = "ObservationStore")
46 public abstract class ObservationStoreEntity {
47 
48     /** The id automatically assigned to the observation batch. */
49     @CopyAnnotations
50     @ColumnInfo(name = "observation_store_id")
51     @PrimaryKey(autoGenerate = true)
52     @NonNull
observationStoreId()53     public abstract int observationStoreId();
54 
55     /** The stored observation batch. */
56     @CopyAnnotations
57     @ColumnInfo(name = "unencrypted_observation_batch")
58     @NonNull
unencryptedObservationBatch()59     public abstract UnencryptedObservationBatch unencryptedObservationBatch();
60 
61     /**
62      * Creates an {@link ObservationStoreEntity}.
63      *
64      * <p>Used by Room to instantiate objects.
65      */
66     @NonNull
create( int observationStoreId, UnencryptedObservationBatch unencryptedObservationBatch)67     public static ObservationStoreEntity create(
68             int observationStoreId, UnencryptedObservationBatch unencryptedObservationBatch) {
69         return new AutoValue_ObservationStoreEntity(
70                 observationStoreId, unencryptedObservationBatch);
71     }
72 
73     /** Creates an {@link ObservationStoreEntity} to insert. */
74     @Ignore
75     @NonNull
createForInsertion( UnencryptedObservationBatch unencryptedObservationBatch)76     static ObservationStoreEntity createForInsertion(
77             UnencryptedObservationBatch unencryptedObservationBatch) {
78         return new AutoValue_ObservationStoreEntity(0 /*unused */, unencryptedObservationBatch);
79     }
80 
81     /**
82      * Creates an {@link ObservationBatch} using the provided {@link Encrypter}.
83      *
84      * @param encrypter the {@link Encrypter} to encrypt data with
85      * @return an ObservationBatch
86      * @throws EncryptionFailedException if encryption failed
87      */
88     @NonNull
encrypt(@onNull Encrypter encrypter)89     public ObservationBatch encrypt(@NonNull Encrypter encrypter) throws EncryptionFailedException {
90         Objects.requireNonNull(encrypter);
91 
92         ObservationBatch.Builder encryptedObservations =
93                 ObservationBatch.newBuilder()
94                         .setMetaData(unencryptedObservationBatch().getMetadata());
95         for (ObservationToEncrypt toEncrypt :
96                 unencryptedObservationBatch().getUnencryptedObservationsList()) {
97             Optional<EncryptedMessage> encryptionResult = encrypter.encryptObservation(toEncrypt);
98             encryptionResult.ifPresent(encryptedObservations::addEncryptedObservation);
99         }
100         return encryptedObservations.build();
101     }
102 }
103