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 */ create( int observationStoreId, UnencryptedObservationBatch unencryptedObservationBatch)66 public static ObservationStoreEntity create( 67 int observationStoreId, UnencryptedObservationBatch unencryptedObservationBatch) { 68 return new AutoValue_ObservationStoreEntity( 69 observationStoreId, unencryptedObservationBatch); 70 } 71 72 /** Creates an {@link ObservationStoreEntity} to insert. */ 73 @Ignore createForInsertion( UnencryptedObservationBatch unencryptedObservationBatch)74 static ObservationStoreEntity createForInsertion( 75 UnencryptedObservationBatch unencryptedObservationBatch) { 76 return new AutoValue_ObservationStoreEntity(0 /*unused */, unencryptedObservationBatch); 77 } 78 79 /** 80 * Creates an {@link ObservationBatch} using the provided {@link Encrypter}. 81 * 82 * @param encrypter the {@link Encrypter} to encrypt data with 83 * @return an ObservationBatch 84 * @throws EncryptionFailedException if encryption failed 85 */ encrypt(Encrypter encrypter)86 public ObservationBatch encrypt(Encrypter encrypter) throws EncryptionFailedException { 87 Objects.requireNonNull(encrypter); 88 89 ObservationBatch.Builder encryptedObservations = 90 ObservationBatch.newBuilder() 91 .setMetaData(unencryptedObservationBatch().getMetadata()); 92 for (ObservationToEncrypt toEncrypt : 93 unencryptedObservationBatch().getUnencryptedObservationsList()) { 94 Optional<EncryptedMessage> encryptionResult = encrypter.encryptObservation(toEncrypt); 95 encryptionResult.ifPresent(encryptedObservations::addEncryptedObservation); 96 } 97 return encryptedObservations.build(); 98 } 99 } 100