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