diff options
| author | 2023-01-06 18:59:47 +0000 | |
|---|---|---|
| committer | 2023-01-06 18:59:47 +0000 | |
| commit | 5cc7f80a84700d224edd83c651fe1e5450a43b16 (patch) | |
| tree | b51744b3337bb8526db40745208e7df58fb09411 | |
| parent | 89046cb92b46afb0e1da70716c10a53870550f43 (diff) | |
| parent | 6c5c89edc575a8a8651eddf540206a39d65300a9 (diff) | |
Merge "Add unit tests for RemoteProvisioningRegistration" am: 6c5c89edc5
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2359449
Change-Id: I9955d8850b8d0d9a31f2fa2b5fae30e6d2d5b9cf
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
6 files changed, 300 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/security/rkp/TEST_MAPPING b/services/core/java/com/android/server/security/rkp/TEST_MAPPING new file mode 100644 index 000000000000..e98396831a1d --- /dev/null +++ b/services/core/java/com/android/server/security/rkp/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "presubmit": [ + { + "name": "RemoteProvisioningServiceTests" + } + ] +} + diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp new file mode 100644 index 000000000000..075680a51af3 --- /dev/null +++ b/services/tests/RemoteProvisioningServiceTests/Android.bp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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 { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "RemoteProvisioningServiceTests", + srcs: ["src/**/*.java"], + static_libs: [ + "android.security.rkp_aidl-java", + "androidx.test.core", + "androidx.test.ext.junit", + "androidx.test.rules", + "mockito-target", + "service-rkp.impl", + "services.core", + "truth-prebuilt", + ], + test_suites: [ + "device-tests", + ], + platform_apis: true, +} diff --git a/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml b/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml new file mode 100644 index 000000000000..7c12e1895e5c --- /dev/null +++ b/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.security.rkp.test"> + + <application android:testOnly="true"> + <uses-library android:name="android.test.runner"/> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.security.rkp.test" + android:label="Remote Provisioning System Service Tests"/> +</manifest> diff --git a/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml b/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml new file mode 100644 index 000000000000..bf86fc8e96d6 --- /dev/null +++ b/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 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. +--> +<configuration description="Runs Frameworks RemoteProvisioningService Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + <option name="test-tag" value="RemoteProvisioningServiceTests" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="RemoteProvisioningServiceTests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.server.security.rkp.test" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" /> + </test> +</configuration> diff --git a/services/tests/RemoteProvisioningServiceTests/OWNERS b/services/tests/RemoteProvisioningServiceTests/OWNERS new file mode 100644 index 000000000000..348f94048311 --- /dev/null +++ b/services/tests/RemoteProvisioningServiceTests/OWNERS @@ -0,0 +1 @@ +file:platform/frameworks/base:master:/core/java/android/security/rkp/OWNERS diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java new file mode 100644 index 000000000000..470f2bec684c --- /dev/null +++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2022 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.android.server.security.rkp; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; +import static org.mockito.AdditionalAnswers.answerVoid; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.os.CancellationSignal; +import android.os.OperationCanceledException; +import android.os.OutcomeReceiver; +import android.security.rkp.IGetKeyCallback; +import android.security.rkp.service.RegistrationProxy; +import android.security.rkp.service.RemotelyProvisionedKey; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.stubbing.Answer; +import org.mockito.stubbing.VoidAnswer4; + +import java.time.Duration; +import java.util.Arrays; +import java.util.concurrent.Executor; + +/** + * Build/Install/Run: + * atest FrameworksServicesTests:RemoteProvisioningRegistrationTest + */ +@RunWith(AndroidJUnit4.class) +public class RemoteProvisioningRegistrationTest { + private RegistrationProxy mRegistrationProxy; + private RemoteProvisioningRegistration mRegistration; + + @Before + public void setUp() { + mRegistrationProxy = mock(RegistrationProxy.class); + mRegistration = new RemoteProvisioningRegistration(mRegistrationProxy, Runnable::run); + } + + // answerVoid wrapper with explicit types, avoiding long signatures when mocking getKeyAsync. + static Answer<Void> answerGetKeyAsync( + VoidAnswer4<Integer, CancellationSignal, Executor, + OutcomeReceiver<RemotelyProvisionedKey, Exception>> answer) { + return answerVoid(answer); + } + + // matcher helper, making it easier to match the different key types + private android.security.rkp.RemotelyProvisionedKey matches( + RemotelyProvisionedKey expectedKey) { + return argThat((android.security.rkp.RemotelyProvisionedKey key) -> + Arrays.equals(key.keyBlob, expectedKey.getKeyBlob()) + && Arrays.equals(key.encodedCertChain, expectedKey.getEncodedCertChain()) + ); + } + + @Test + public void getKeySuccess() throws Exception { + RemotelyProvisionedKey expectedKey = mock(RemotelyProvisionedKey.class); + doAnswer( + answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> + executor.execute(() -> receiver.onResult(expectedKey)))) + .when(mRegistrationProxy).getKeyAsync(eq(42), any(), any(), any()); + + IGetKeyCallback callback = mock(IGetKeyCallback.class); + mRegistration.getKey(42, callback); + verify(callback).onSuccess(matches(expectedKey)); + verifyNoMoreInteractions(callback); + } + + @Test + public void getKeyHandlesError() throws Exception { + Exception expectedException = new Exception("oops!"); + doAnswer( + answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> + executor.execute(() -> receiver.onError(expectedException)))) + .when(mRegistrationProxy).getKeyAsync(eq(0), any(), any(), any()); + IGetKeyCallback callback = mock(IGetKeyCallback.class); + mRegistration.getKey(0, callback); + verify(callback).onError(eq(expectedException.getMessage())); + verifyNoMoreInteractions(callback); + } + + @Test + public void getKeyCancelDuringProxyOperation() throws Exception { + IGetKeyCallback callback = mock(IGetKeyCallback.class); + doAnswer( + answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> { + mRegistration.cancelGetKey(callback); + assertThat(cancelSignal.isCanceled()).isTrue(); + executor.execute(() -> receiver.onError(new OperationCanceledException())); + })) + .when(mRegistrationProxy).getKeyAsync(eq(Integer.MAX_VALUE), any(), any(), any()); + + mRegistration.getKey(Integer.MAX_VALUE, callback); + verify(callback).onCancel(); + verifyNoMoreInteractions(callback); + } + + @Test + public void cancelGetKeyWithInvalidCallback() throws Exception { + IGetKeyCallback callback = mock(IGetKeyCallback.class); + assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback)); + } + + @Test + public void getKeyRejectsDuplicateCallback() throws Exception { + IGetKeyCallback callback = mock(IGetKeyCallback.class); + doAnswer( + answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> { + assertThrows(IllegalArgumentException.class, () -> + mRegistration.getKey(0, callback)); + executor.execute(() -> receiver.onResult(mock(RemotelyProvisionedKey.class))); + })) + .when(mRegistrationProxy).getKeyAsync(anyInt(), any(), any(), any()); + + mRegistration.getKey(0, callback); + verify(callback, times(1)).onSuccess(any()); + verifyNoMoreInteractions(callback); + } + + @Test + public void getKeyCancelAfterCompleteFails() throws Exception { + IGetKeyCallback callback = mock(IGetKeyCallback.class); + doAnswer( + answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> + executor.execute(() -> + receiver.onResult(mock(RemotelyProvisionedKey.class)) + ))) + .when(mRegistrationProxy).getKeyAsync(eq(Integer.MIN_VALUE), any(), any(), any()); + + mRegistration.getKey(Integer.MIN_VALUE, callback); + verify(callback).onSuccess(any()); + assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback)); + verifyNoMoreInteractions(callback); + } + + @Test + public void getKeyCatchesExceptionFromProxy() throws Exception { + Exception expectedException = new RuntimeException("oops! bad input!"); + doThrow(expectedException) + .when(mRegistrationProxy) + .getKeyAsync(anyInt(), any(), any(), any()); + + IGetKeyCallback callback = mock(IGetKeyCallback.class); + mRegistration.getKey(0, callback); + verify(callback).onError(eq(expectedException.getMessage())); + assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback)); + verifyNoMoreInteractions(callback); + } + + @Test + public void storeUpgradedKeySuccess() throws Exception { + // TODO(b/262748535) + } + + @Test + public void storeUpgradedKeyFails() throws Exception { + // TODO(b/262748535) + } + + @Test + public void storeUpgradedCatchesExceptionFromProxy() throws Exception { + // TODO(b/262748535) + } +} |