| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.android.connecteddevice.storage; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.util.concurrent.MoreExecutors.directExecutor; |
| import static org.junit.Assert.assertThrows; |
| |
| import androidx.room.Room; |
| import android.content.Context; |
| import android.util.Base64; |
| import android.util.Pair; |
| import androidx.test.core.app.ApplicationProvider; |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import com.google.android.connecteddevice.model.AssociatedDevice; |
| import com.google.android.connecteddevice.util.ByteUtils; |
| import java.security.InvalidParameterException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.UUID; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| @RunWith(AndroidJUnit4.class) |
| public final class ConnectedDeviceStorageTest { |
| private static final int ACTIVE_USER_ID = 10; |
| |
| private final Context context = ApplicationProvider.getApplicationContext(); |
| |
| private ConnectedDeviceStorage connectedDeviceStorage; |
| |
| private List<Pair<Integer, AssociatedDevice>> addedAssociatedDevices; |
| |
| @Before |
| public void setUp() { |
| AssociatedDeviceDao database = |
| Room.inMemoryDatabaseBuilder(context, ConnectedDeviceDatabase.class) |
| .allowMainThreadQueries() |
| .setQueryExecutor(directExecutor()) |
| .build() |
| .associatedDeviceDao(); |
| |
| connectedDeviceStorage = new ConnectedDeviceStorage(context, new FakeCryptoHelper(), database); |
| addedAssociatedDevices = new ArrayList<>(); |
| } |
| |
| @After |
| public void tearDown() { |
| // Clear any associated devices added during tests. |
| for (Pair<Integer, AssociatedDevice> device : addedAssociatedDevices) { |
| connectedDeviceStorage.removeAssociatedDevice(device.first, device.second.getDeviceId()); |
| } |
| } |
| |
| @Test |
| public void getAssociatedDeviceIdsForUser_includesNewlyAddedDevice() { |
| AssociatedDevice addedDevice = addRandomAssociatedDevice(ACTIVE_USER_ID); |
| List<String> associatedDevices = |
| connectedDeviceStorage.getAssociatedDeviceIdsForUser(ACTIVE_USER_ID); |
| assertThat(associatedDevices).containsExactly(addedDevice.getDeviceId()); |
| } |
| |
| @Test |
| public void getAssociatedDeviceIdsForUser_excludesDeviceAddedForOtherUser() { |
| addRandomAssociatedDevice(ACTIVE_USER_ID); |
| List<String> associatedDevices = |
| connectedDeviceStorage.getAssociatedDeviceIdsForUser(ACTIVE_USER_ID + 1); |
| assertThat(associatedDevices).isEmpty(); |
| } |
| |
| @Test |
| public void getAssociatedDeviceIdsForUser_excludesRemovedDevice() { |
| AssociatedDevice addedDevice = addRandomAssociatedDevice(ACTIVE_USER_ID); |
| connectedDeviceStorage.removeAssociatedDevice(ACTIVE_USER_ID, addedDevice.getDeviceId()); |
| List<String> associatedDevices = |
| connectedDeviceStorage.getAssociatedDeviceIdsForUser(ACTIVE_USER_ID); |
| assertThat(associatedDevices).isEmpty(); |
| } |
| |
| @Test |
| public void getAssociatedDevicesForUser_includesNewlyAddedDevice() { |
| AssociatedDevice addedDevice = addRandomAssociatedDevice(ACTIVE_USER_ID); |
| List<AssociatedDevice> associatedDevices = |
| connectedDeviceStorage.getAssociatedDevicesForUser(ACTIVE_USER_ID); |
| assertThat(associatedDevices).containsExactly(addedDevice); |
| } |
| |
| @Test |
| public void getAssociatedDevicesForUser_excludesDeviceAddedForOtherUser() { |
| addRandomAssociatedDevice(ACTIVE_USER_ID); |
| List<String> associatedDevices = |
| connectedDeviceStorage.getAssociatedDeviceIdsForUser(ACTIVE_USER_ID + 1); |
| assertThat(associatedDevices).isEmpty(); |
| } |
| |
| @Test |
| public void getAssociatedDevicesForUser_excludesRemovedDevice() { |
| AssociatedDevice addedDevice = addRandomAssociatedDevice(ACTIVE_USER_ID); |
| connectedDeviceStorage.removeAssociatedDevice(ACTIVE_USER_ID, addedDevice.getDeviceId()); |
| List<AssociatedDevice> associatedDevices = |
| connectedDeviceStorage.getAssociatedDevicesForUser(ACTIVE_USER_ID); |
| assertThat(associatedDevices).isEmpty(); |
| } |
| |
| @Test |
| public void getEncryptionKey_returnsSavedKey() { |
| String deviceId = addRandomAssociatedDevice(ACTIVE_USER_ID).getDeviceId(); |
| byte[] key = ByteUtils.randomBytes(16); |
| connectedDeviceStorage.saveEncryptionKey(deviceId, key); |
| assertThat(connectedDeviceStorage.getEncryptionKey(deviceId)).isEqualTo(key); |
| } |
| |
| @Test |
| public void getEncryptionKey_returnsNullForUnrecognizedDeviceId() { |
| String deviceId = addRandomAssociatedDevice(ACTIVE_USER_ID).getDeviceId(); |
| connectedDeviceStorage.saveEncryptionKey(deviceId, ByteUtils.randomBytes(16)); |
| assertThat(connectedDeviceStorage.getEncryptionKey(UUID.randomUUID().toString())).isNull(); |
| } |
| |
| @Test |
| public void saveChallengeSecret_throwsForInvalidLengthSecret() { |
| byte[] invalidSecret = ByteUtils.randomBytes(ConnectedDeviceStorage.CHALLENGE_SECRET_BYTES - 1); |
| assertThrows( |
| InvalidParameterException.class, |
| () -> |
| connectedDeviceStorage.saveChallengeSecret( |
| UUID.randomUUID().toString(), invalidSecret)); |
| } |
| |
| private AssociatedDevice addRandomAssociatedDevice(int userId) { |
| AssociatedDevice device = |
| new AssociatedDevice( |
| UUID.randomUUID().toString(), |
| "00:00:00:00:00:00", |
| "Test Device", |
| /* isConnectionEnabled= */ true); |
| addAssociatedDevice(userId, device, ByteUtils.randomBytes(16)); |
| return device; |
| } |
| |
| private void addAssociatedDevice(int userId, AssociatedDevice device, byte[] encryptionKey) { |
| connectedDeviceStorage.addAssociatedDeviceForUser(userId, device); |
| connectedDeviceStorage.saveEncryptionKey(device.getDeviceId(), encryptionKey); |
| addedAssociatedDevices.add(new Pair<>(userId, device)); |
| } |
| |
| /** A {@link CryptoHelper} that does base64 de/encoding to simulate encryption. */ |
| private static class FakeCryptoHelper implements CryptoHelper { |
| @Override |
| public String encrypt(byte[] value) { |
| return Base64.encodeToString(value, Base64.DEFAULT); |
| } |
| |
| @Override |
| public byte[] decrypt(String value) { |
| return Base64.decode(value, Base64.DEFAULT); |
| } |
| } |
| } |