Create the system service for AVF & hook the manager to it.

Bug: 201696614
Change-Id: I4a7a830ba809ed59a030a87c4f479199685d8a42
Test: atest AttestationVerificationTest
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 0476307..089c269 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -200,6 +200,8 @@
 import android.scheduling.SchedulingFrameworkInitializer;
 import android.security.FileIntegrityManager;
 import android.security.IFileIntegrityService;
+import android.security.attestationverification.AttestationVerificationManager;
+import android.security.attestationverification.IAttestationVerificationManagerService;
 import android.service.oemlock.IOemLockService;
 import android.service.oemlock.OemLockManager;
 import android.service.persistentdata.IPersistentDataBlockService;
@@ -1425,6 +1427,19 @@
                         return new FileIntegrityManager(ctx.getOuterContext(),
                                 IFileIntegrityService.Stub.asInterface(b));
                     }});
+
+        registerService(Context.ATTESTATION_VERIFICATION_SERVICE,
+                AttestationVerificationManager.class,
+                new CachedServiceFetcher<AttestationVerificationManager>() {
+                    @Override
+                    public AttestationVerificationManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(
+                                Context.ATTESTATION_VERIFICATION_SERVICE);
+                        return new AttestationVerificationManager(ctx.getOuterContext(),
+                                IAttestationVerificationManagerService.Stub.asInterface(b));
+                    }});
+
         //CHECKSTYLE:ON IndentationCheck
         registerService(Context.APP_INTEGRITY_SERVICE, AppIntegrityManager.class,
                 new CachedServiceFetcher<AppIntegrityManager>() {
diff --git a/core/java/android/security/attestationverification/AttestationVerificationManager.java b/core/java/android/security/attestationverification/AttestationVerificationManager.java
index 8ed5b20..db783ce 100644
--- a/core/java/android/security/attestationverification/AttestationVerificationManager.java
+++ b/core/java/android/security/attestationverification/AttestationVerificationManager.java
@@ -26,12 +26,19 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.Bundle;
+import android.os.ParcelDuration;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.infra.AndroidFuture;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.time.Duration;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
 
 /**
@@ -43,6 +50,12 @@
 @SystemService(Context.ATTESTATION_VERIFICATION_SERVICE)
 public class AttestationVerificationManager {
 
+    private static final String TAG = "AVF";
+    private static final Duration MAX_TOKEN_AGE = Duration.ofHours(1);
+
+    private final Context mContext;
+    private final IAttestationVerificationManagerService mService;
+
     /**
      * Verifies that {@code attestation} describes a computing environment that meets the
      * requirements of {@code profile}, {@code localBindingType}, and {@code requirements}.
@@ -96,7 +109,21 @@
             @NonNull byte[] attestation,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BiConsumer<@VerificationResult Integer, VerificationToken> callback) {
-        executor.execute(() -> callback.accept(RESULT_UNKNOWN, null));
+        try {
+            AndroidFuture<IVerificationResult> resultCallback = new AndroidFuture<>();
+            resultCallback.thenAccept(result -> {
+                Log.d(TAG, "verifyAttestation result: " + result.resultCode + " / " + result.token);
+                executor.execute(() -> {
+                    callback.accept(result.resultCode, result.token);
+                });
+            });
+
+            mService.verifyAttestation(profile, localBindingType, requirements, attestation,
+                    resultCallback);
+
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -134,12 +161,38 @@
             @LocalBindingType int localBindingType,
             @NonNull Bundle requirements,
             @NonNull VerificationToken token,
-            @Nullable java.time.Duration maximumAge) {
-        return RESULT_UNKNOWN;
+            @Nullable Duration maximumAge) {
+        Duration usedMaximumAge;
+        if (maximumAge == null) {
+            usedMaximumAge = MAX_TOKEN_AGE;
+        } else {
+            if (maximumAge.compareTo(MAX_TOKEN_AGE) > 0) {
+                throw new IllegalArgumentException(
+                        "maximumAge cannot be greater than " + MAX_TOKEN_AGE + "; was "
+                                + maximumAge);
+            }
+            usedMaximumAge = maximumAge;
+        }
+
+        try {
+            AndroidFuture<Integer> resultCallback = new AndroidFuture<>();
+            resultCallback.orTimeout(5, TimeUnit.SECONDS);
+
+            mService.verifyToken(token, new ParcelDuration(usedMaximumAge), resultCallback);
+            return resultCallback.get(); // block on result callback
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (Throwable t) {
+            throw new RuntimeException("Error verifying token.", t);
+        }
     }
 
     /** @hide */
-    public AttestationVerificationManager() {
+    public AttestationVerificationManager(
+            @NonNull Context context,
+            @NonNull IAttestationVerificationManagerService service) {
+        this.mContext = context;
+        this.mService = service;
     }
 
     /** @hide */
diff --git a/core/java/android/security/attestationverification/IAttestationVerificationManagerService.aidl b/core/java/android/security/attestationverification/IAttestationVerificationManagerService.aidl
new file mode 100644
index 0000000..2fb328c
--- /dev/null
+++ b/core/java/android/security/attestationverification/IAttestationVerificationManagerService.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 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 android.security.attestationverification;
+
+import android.os.Bundle;
+import android.os.ParcelDuration;
+import android.security.attestationverification.AttestationProfile;
+import android.security.attestationverification.VerificationToken;
+import com.android.internal.infra.AndroidFuture;
+
+
+/**
+ * Binder interface to communicate with AttestationVerificationManagerService.
+ * @hide
+ */
+oneway interface IAttestationVerificationManagerService {
+
+    void verifyAttestation(
+            in AttestationProfile profile,
+            in int localBindingType,
+            in Bundle requirements,
+            in byte[] attestation,
+            in AndroidFuture resultCallback);
+
+    void verifyToken(
+            in VerificationToken token,
+            in ParcelDuration maximumTokenAge,
+            in AndroidFuture resultCallback);
+}
diff --git a/core/java/android/security/attestationverification/IAttestationVerificationService.aidl b/core/java/android/security/attestationverification/IAttestationVerificationService.aidl
new file mode 100644
index 0000000..082ad32
--- /dev/null
+++ b/core/java/android/security/attestationverification/IAttestationVerificationService.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 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 android.security.attestationverification;
+
+import android.os.Bundle;
+import com.android.internal.infra.AndroidFuture;
+
+
+/**
+ * Binder interface for the system server to communicate with app implementations of
+ * AttestationVerificationService.
+ * @hide
+ */
+oneway interface IAttestationVerificationService {
+    void onVerifyAttestation(
+        in Bundle requirements,
+        in byte[] attestation,
+        in AndroidFuture callback);
+}
diff --git a/core/java/android/security/attestationverification/IVerificationResult.aidl b/core/java/android/security/attestationverification/IVerificationResult.aidl
new file mode 100644
index 0000000..f61c456
--- /dev/null
+++ b/core/java/android/security/attestationverification/IVerificationResult.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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 android.security.attestationverification;
+
+import android.security.attestationverification.VerificationToken;
+
+
+/**
+ * The result of an attestation verification.
+ *
+ * {@hide}
+ */
+parcelable IVerificationResult {
+    /** The result code corresponding to @VerificationResult. */
+    int resultCode;
+    /** The token for the verification or null. */
+    VerificationToken token;
+}
diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
new file mode 100644
index 0000000..f519ced
--- /dev/null
+++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 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;
+
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelDuration;
+import android.os.RemoteException;
+import android.security.attestationverification.AttestationProfile;
+import android.security.attestationverification.IAttestationVerificationManagerService;
+import android.security.attestationverification.IVerificationResult;
+import android.security.attestationverification.VerificationToken;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.server.SystemService;
+
+/**
+ * A {@link SystemService} which provides functionality related to verifying attestations of
+ * (usually) remote computing environments.
+ *
+ * @hide
+ */
+public class AttestationVerificationManagerService extends SystemService {
+
+    private static final String TAG = "AVF";
+
+    public AttestationVerificationManagerService(final Context context) {
+        super(context);
+    }
+
+    private final IBinder mService = new IAttestationVerificationManagerService.Stub() {
+        @Override
+        public void verifyAttestation(
+                AttestationProfile profile,
+                int localBindingType,
+                Bundle requirements,
+                byte[] attestation,
+                AndroidFuture resultCallback) throws RemoteException {
+            try {
+                Slog.d(TAG, "verifyAttestation");
+                verifyAttestationForAllVerifiers(profile, localBindingType, requirements,
+                        attestation, resultCallback);
+            } catch (Throwable t) {
+                Slog.e(TAG, "failed to verify attestation", t);
+                throw ExceptionUtils.propagate(t, RemoteException.class);
+            }
+        }
+
+        @Override
+        public void verifyToken(VerificationToken token, ParcelDuration parcelDuration,
+                AndroidFuture resultCallback) throws RemoteException {
+            // TODO(b/201696614): Implement
+            resultCallback.complete(RESULT_UNKNOWN);
+        }
+    };
+
+    private void verifyAttestationForAllVerifiers(
+            AttestationProfile profile, int localBindingType, Bundle requirements,
+            byte[] attestation, AndroidFuture<IVerificationResult> resultCallback) {
+        // TODO(b/201696614): Implement
+        IVerificationResult result = new IVerificationResult();
+        result.resultCode = RESULT_UNKNOWN;
+        result.token = null;
+        resultCallback.complete(result);
+    }
+
+    @Override
+    public void onStart() {
+        Slog.d(TAG, "Started");
+        publishBinderService(Context.ATTESTATION_VERIFICATION_SERVICE, mService);
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3ede408..a72cf3a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -176,6 +176,7 @@
 import com.android.server.restrictions.RestrictionsManagerService;
 import com.android.server.role.RoleServicePlatformHelper;
 import com.android.server.rotationresolver.RotationResolverManagerService;
+import com.android.server.security.AttestationVerificationManagerService;
 import com.android.server.security.FileIntegrityService;
 import com.android.server.security.KeyAttestationApplicationIdProviderService;
 import com.android.server.security.KeyChainSystemService;
@@ -2339,6 +2340,10 @@
                 t.traceEnd();
             }
 
+            t.traceBegin("StartAttestationVerificationService");
+            mSystemServiceManager.startService(AttestationVerificationManagerService.class);
+            t.traceEnd();
+
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
                 t.traceBegin("StartCompanionDeviceManager");
                 mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS);
diff --git a/tests/AttestationVerificationTest/Android.bp b/tests/AttestationVerificationTest/Android.bp
new file mode 100644
index 0000000..a4741eed
--- /dev/null
+++ b/tests/AttestationVerificationTest/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "AttestationVerificationTest",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    defaults: ["cts_defaults"],
+    manifest: "AndroidManifest.xml",
+    test_config: "AndroidTest.xml",
+    platform_apis: true,
+    certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
+    test_suites: ["device-tests"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "platform-test-annotations",
+    ],
+}
diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml
new file mode 100755
index 0000000..c42bde9
--- /dev/null
+++ b/tests/AttestationVerificationTest/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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="android.security.attestationverification">
+
+    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+    <uses-permission android:name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE" />
+
+    <application>
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name=".SystemAttestationVerificationTest$TestActivity" />
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.security.attestationverification">
+    </instrumentation>
+</manifest>
diff --git a/tests/AttestationVerificationTest/AndroidTest.xml b/tests/AttestationVerificationTest/AndroidTest.xml
new file mode 100644
index 0000000..1325760
--- /dev/null
+++ b/tests/AttestationVerificationTest/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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="Platform tests for Attestation Verification Framework">
+    <option name="test-tag" value="AttestationVerificationTest" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="AttestationVerificationTest.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.security.attestationverification" />
+        <option name="hidden-api-checks" value="false" />
+    </test>
+</configuration>
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
new file mode 100644
index 0000000..48bfd6f
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
@@ -0,0 +1,90 @@
+package android.security.attestationverification
+
+import android.os.Bundle
+import android.app.Activity
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import com.google.common.truth.Truth.assertThat
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
+import java.lang.IllegalArgumentException
+import java.time.Duration
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+
+/** Test for system-defined attestation verifiers. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SystemAttestationVerificationTest {
+
+    @get:Rule
+    val rule = ActivityScenarioRule(TestActivity::class.java)
+
+    private lateinit var activity: Activity
+    private lateinit var avm: AttestationVerificationManager
+
+    @Before
+    fun setup() {
+        rule.getScenario().onActivity {
+            avm = it.getSystemService(AttestationVerificationManager::class.java)
+            activity = it
+        }
+    }
+
+    @Test
+    fun verifyAttestation_returnsUnknown() {
+        val future = CompletableFuture<Int>()
+        val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+        avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+                activity.mainExecutor) { result, _ ->
+            future.complete(result)
+        }
+
+        assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
+    }
+
+    @Test
+    fun verifyToken_returnsUnknown() {
+        val future = CompletableFuture<Int>()
+        val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+        avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+                activity.mainExecutor) { _, token ->
+            val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
+            future.complete(result)
+        }
+
+        assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
+    }
+
+    @Test
+    fun verifyToken_tooBigMaxAgeThrows() {
+        val future = CompletableFuture<VerificationToken>()
+        val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+        avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+                activity.mainExecutor) { _, token ->
+            future.complete(token)
+        }
+
+        assertThrows(IllegalArgumentException::class.java) {
+            avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), future.getSoon(),
+                    Duration.ofSeconds(3601))
+        }
+    }
+
+    private fun <T> CompletableFuture<T>.getSoon(): T {
+        return this.get(1, TimeUnit.SECONDS)
+    }
+
+    class TestActivity : Activity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            super.onCreate(savedInstanceState)
+        }
+    }
+}