Keystore 2.0: Implement APC service.

This patch implements the Android Protected Confirmation service in
Keystore 2.0. This includes a C++ wrapper around the HIDL confirmationui
interface which will stay a HIDL interface for now.
This patch also includes the new AIDL specification.

This patch lacks death listener registration b/176491050.

Bug: 159341464
Bug: 173546269
Test: None
Change-Id: Ida4af108e86b538ab64d1dea4809cfa3b36f74cd
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 250646a..883271d 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -18,10 +18,12 @@
     srcs: ["src/lib.rs"],
 
     rustlibs: [
-        "android.system.keystore2-rust",
         "android.hardware.security.keymint-rust",
+        "android.security.apc-rust",
+        "android.system.keystore2-rust",
         "libanyhow",
         "libbinder_rs",
+        "libkeystore2_apc_compat-rust",
         "libkeystore2_crypto_rust",
         "libkeystore2_selinux",
         "liblazy_static",
@@ -42,11 +44,13 @@
     auto_gen_config: false,
     test_config: "AndroidTest.xml",
     rustlibs: [
-        "android.system.keystore2-rust",
         "android.hardware.security.keymint-rust",
+        "android.security.apc-rust",
+        "android.system.keystore2-rust",
         "libandroid_logger",
         "libanyhow",
         "libbinder_rs",
+        "libkeystore2_apc_compat-rust",
         "libkeystore2_crypto_rust",
         "libkeystore2_selinux",
         "liblazy_static",
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index 00be2b7..3051173 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -14,9 +14,7 @@
 
 aidl_interface {
     name: "android.security.attestationmanager",
-    srcs: [
-        "android/security/attestationmanager/*.aidl",
-    ],
+    srcs: [ "android/security/attestationmanager/*.aidl", ],
     imports: [ "android.hardware.security.keymint" ],
     unstable: true,
     backend: {
@@ -47,3 +45,16 @@
     },
 }
 
+aidl_interface {
+    name: "android.security.apc",
+    srcs: [ "android/security/apc/*.aidl" ],
+    unstable: true,
+    backend: {
+        java: {
+            enabled: true,
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
diff --git a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
new file mode 100644
index 0000000..f47d7f5
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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 android.security.apc;
+
+import android.security.apc.ResponseCode;
+
+/**
+ * This callback interface must be implemented by the client to receive the result of the user
+ * confirmation.
+ */
+interface IConfirmationCallback {
+    /**
+     * This callback gets called by the implementing service when a pending confirmation prompt
+     * gets finalized.
+     *
+     * @param result
+     *  - ResponseCode.OK On success. In this case dataConfirmed must be non null.
+     *  - ResponseCode.CANCELLED If the user cancelled the prompt. In this case dataConfirmed must
+     *           be null.
+     *  - ResponseCode.ABORTED If the client called IProtectedConfirmation.cancelPrompt() or if the
+     *           prompt was cancelled by the system due to an asynchronous event. In this case
+     *           dataConfirmed must be null.
+     *
+     * @param dataConfirmed This is the message that was confirmed and for which a confirmation
+     *           token is now available in implementing service. A subsequent attempt to sign this
+     *           message with a confirmation bound key will succeed. The message is a CBOR map
+     *           including the prompt text and the extra data.
+     */
+    oneway void onCompleted(in ResponseCode result, in @nullable byte[] dataConfirmed);
+}
diff --git a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
new file mode 100644
index 0000000..26ccf0f
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
@@ -0,0 +1,70 @@
+/*
+ * 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 android.security.apc;
+
+import android.security.apc.IConfirmationCallback;
+
+interface IProtectedConfirmation {
+
+    /**
+     * When set in the uiOptionFlags parameter of presentPrompt, indicates to the implementation
+     * that it shall use inverted color mode.
+     */
+    const int FLAG_UI_OPTION_INVERTED = 1;
+    /**
+     * When set in the uiOptionFlags parameter of presentPrompt, indicates to the implementation
+     * that it shall use magnified font mode.
+     */
+    const int FLAG_UI_OPTION_MAGNIFIED = 2;
+
+    /**
+     * Present the confirmation prompt. The caller must implement IConfirmationCallback and pass
+     * it to this function as listener.
+     *
+     * @param listener Must implement IConfirmationCallback. Doubles as session identifier when
+     *           passed to cancelPrompt.
+     * @param promptText The text that will be displayed to the user using the protected
+     *           confirmation UI.
+     * @param extraData Extra data, e.g., a nonce, that will be included in the to-be-signed
+     *           message.
+     * @param locale The locale string is used to select the language for the instructions
+     *           displayed by the confirmation prompt.
+     * @param uiOptionFlags Bitwise combination of FLAG_UI_OPTION_* see above.
+     *
+     * Service specific error codes:
+     *  - ResponseCode.OPERATION_PENDING If another prompt is already pending.
+     *  - ResponseCode.SYSTEM_ERROR An unexpected error occurred.
+     */
+    void presentPrompt(in IConfirmationCallback listener, in String promptText,
+            in byte[] extraData, in String locale, in int uiOptionFlags);
+
+    /**
+     * Cancel an ongoing prompt.
+     *
+     * @param listener Must implement IConfirmationCallback, although in this context this binder
+     *            token is only used to identify the session that is to be cancelled.
+     *
+     * Service specific error code:
+     *  - ResponseCode.IGNORED If the listener does not represent an ongoing prompt session.
+     */
+    void cancelPrompt(IConfirmationCallback listener);
+
+    /**
+     * Returns true if the device supports Android Protected Confirmation.
+     */
+    boolean isSupported();
+}
diff --git a/keystore2/aidl/android/security/apc/ResponseCode.aidl b/keystore2/aidl/android/security/apc/ResponseCode.aidl
new file mode 100644
index 0000000..7ae3e1c
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/ResponseCode.aidl
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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 android.security.apc;
+
+/**
+ * Used as service specific exception code by IProtectedConfirmation and as result
+ * code by IConfirmationCallback
+ */
+@Backing(type="int")
+enum ResponseCode {
+    /**
+     * The prompt completed successfully with the user confirming the message (callback result).
+     */
+    OK = 0,
+    /**
+     * The user cancelled the TUI (callback result).
+     */
+    CANCELLED = 1,
+    /**
+     * The prompt was aborted (callback result). This may happen when the app cancels the prompt,
+     * or when the prompt was cancelled due to an unexpected asynchronous event, such as an
+     * incoming phone call.
+     */
+    ABORTED = 2,
+    /**
+     * Another prompt cannot be started because another prompt is pending.
+     */
+    OPERATION_PENDING = 3,
+    /**
+     * The request was ignored.
+     */
+    IGNORED = 4,
+    /**
+     * An unexpected system error occurred.
+     */
+    SYSTEM_ERROR = 5,
+    /**
+     * Backend is not implemented.
+     */
+    UNIMPLEMENTED = 6,
+    /**
+     * Permission Denied.
+     */
+    PERMISSION_DENIED = 30,
+}
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
new file mode 100644
index 0000000..405e9b8
--- /dev/null
+++ b/keystore2/apc_compat/Android.bp
@@ -0,0 +1,56 @@
+// Copyright 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.
+
+cc_library {
+    name: "libkeystore2_apc_compat",
+    srcs: [
+        "apc_compat.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.confirmationui@1.0",
+        "libbase",
+        "libhidlbase",
+        "libutils",
+    ],
+}
+
+rust_bindgen {
+    name: "libkeystore2_apc_compat_bindgen",
+    wrapper_src: "apc_compat.hpp",
+    crate_name: "keystore2_apc_compat_bindgen",
+    source_stem: "bindings",
+
+    bindgen_flags: [
+        "--whitelist-function=tryGetUserConfirmationService",
+        "--whitelist-function=promptUserConfirmation",
+        "--whitelist-function=abortUserConfirmation",
+        "--whitelist-function=closeUserConfirmationService",
+        "--whitelist-var=INVALID_SERVICE_HANDLE",
+        "--whitelist-var=APC_COMPAT_.*",
+    ],
+}
+
+rust_library {
+    name: "libkeystore2_apc_compat-rust",
+    crate_name: "keystore2_apc_compat",
+    srcs: [
+        "apc_compat.rs",
+    ],
+    rustlibs: [
+        "libkeystore2_apc_compat_bindgen",
+    ],
+    shared_libs: [
+        "libkeystore2_apc_compat",
+    ],
+}
diff --git a/keystore2/apc_compat/apc_compat.cpp b/keystore2/apc_compat/apc_compat.cpp
new file mode 100644
index 0000000..08a8e45
--- /dev/null
+++ b/keystore2/apc_compat/apc_compat.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+#include "apc_compat.hpp"
+#include <android-base/logging.h>
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <hwbinder/IBinder.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#define LOG_TAG "keystore2_apc_compat"
+
+namespace keystore2 {
+
+using android::sp;
+using android::hardware::hidl_death_recipient;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Status;
+using android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
+using android::hardware::confirmationui::V1_0::IConfirmationUI;
+using android::hardware::confirmationui::V1_0::ResponseCode;
+using android::hardware::confirmationui::V1_0::UIOption;
+
+static uint32_t responseCode2Compat(ResponseCode rc) {
+    switch (rc) {
+    case ResponseCode::OK:
+        return APC_COMPAT_ERROR_OK;
+    case ResponseCode::Canceled:
+        return APC_COMPAT_ERROR_CANCELLED;
+    case ResponseCode::Aborted:
+        return APC_COMPAT_ERROR_ABORTED;
+    case ResponseCode::OperationPending:
+        return APC_COMPAT_ERROR_OPERATION_PENDING;
+    case ResponseCode::Ignored:
+        return APC_COMPAT_ERROR_IGNORED;
+    case ResponseCode::SystemError:
+    case ResponseCode::Unimplemented:
+    case ResponseCode::Unexpected:
+    case ResponseCode::UIError:
+    case ResponseCode::UIErrorMissingGlyph:
+    case ResponseCode::UIErrorMessageTooLong:
+    case ResponseCode::UIErrorMalformedUTF8Encoding:
+    default:
+        return APC_COMPAT_ERROR_SYSTEM_ERROR;
+    }
+}
+
+class ConfuiCompatSession : public IConfirmationResultCallback, public hidl_death_recipient {
+  public:
+    static sp<ConfuiCompatSession>* tryGetService() {
+        sp<IConfirmationUI> service = IConfirmationUI::tryGetService();
+        if (service) {
+            return new sp(new ConfuiCompatSession(std::move(service)));
+        } else {
+            return nullptr;
+        }
+    }
+
+    uint32_t promptUserConfirmation(ApcCompatCallback callback, const char* prompt_text,
+                                    const uint8_t* extra_data, size_t extra_data_size,
+                                    const char* locale, ApcCompatUiOptions ui_options) {
+        std::string hidl_prompt(prompt_text);
+        std::vector<uint8_t> hidl_extra(extra_data, extra_data + extra_data_size);
+        std::string hidl_locale(locale);
+        std::vector<UIOption> hidl_ui_options;
+        if (ui_options.inverted) {
+            hidl_ui_options.push_back(UIOption::AccessibilityInverted);
+        }
+        if (ui_options.magnified) {
+            hidl_ui_options.push_back(UIOption::AccessibilityMagnified);
+        }
+        auto lock = std::lock_guard(callback_lock_);
+        if (callback_.result != nullptr) {
+            return APC_COMPAT_ERROR_OPERATION_PENDING;
+        }
+        auto err = service_->linkToDeath(sp(this), 0);
+        if (!err.isOk()) {
+            LOG(ERROR) << "Communication error: promptUserConfirmation: "
+                          "Trying to register death recipient: "
+                       << err.description();
+            return APC_COMPAT_ERROR_SYSTEM_ERROR;
+        }
+
+        auto rc = service_->promptUserConfirmation(sp(this), hidl_prompt, hidl_extra, hidl_locale,
+                                                   hidl_ui_options);
+        if (!rc.isOk()) {
+            LOG(ERROR) << "Communication error: promptUserConfirmation: " << rc.description();
+        }
+        if (rc == ResponseCode::OK) {
+            callback_ = callback;
+        }
+        return responseCode2Compat(rc.withDefault(ResponseCode::SystemError));
+    }
+
+    void abort() { service_->abort(); }
+
+    void
+    finalize(ResponseCode responseCode,
+             std::optional<std::reference_wrapper<const hidl_vec<uint8_t>>> dataConfirmed,
+             std::optional<std::reference_wrapper<const hidl_vec<uint8_t>>> confirmationToken) {
+        ApcCompatCallback callback;
+        {
+            auto lock = std::lock_guard(callback_lock_);
+            // Calling the callback consumes the callback data structure. We have to make
+            // sure that it can only be called once.
+            callback = callback_;
+            callback_ = {nullptr, nullptr};
+            // Unlock the callback_lock_ here. It must never be held while calling the callback.
+        }
+
+        if (callback.result != nullptr) {
+            service_->unlinkToDeath(sp(this));
+
+            size_t dataConfirmedSize = 0;
+            const uint8_t* dataConfirmedPtr = nullptr;
+            size_t confirmationTokenSize = 0;
+            const uint8_t* confirmationTokenPtr = nullptr;
+            if (responseCode == ResponseCode::OK) {
+                if (dataConfirmed) {
+                    dataConfirmedPtr = dataConfirmed->get().data();
+                    dataConfirmedSize = dataConfirmed->get().size();
+                }
+                if (dataConfirmed) {
+                    confirmationTokenPtr = confirmationToken->get().data();
+                    confirmationTokenSize = confirmationToken->get().size();
+                }
+            }
+            callback.result(callback.data, responseCode2Compat(responseCode), dataConfirmedPtr,
+                            dataConfirmedSize, confirmationTokenPtr, confirmationTokenSize);
+        }
+    }
+
+    // IConfirmationResultCallback overrides:
+    android::hardware::Return<void> result(ResponseCode responseCode,
+                                           const hidl_vec<uint8_t>& dataConfirmed,
+                                           const hidl_vec<uint8_t>& confirmationToken) override {
+        finalize(responseCode, dataConfirmed, confirmationToken);
+        return Status::ok();
+    };
+
+    void serviceDied(uint64_t /* cookie */,
+                     const ::android::wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+        finalize(ResponseCode::SystemError, {}, {});
+    }
+
+  private:
+    ConfuiCompatSession(sp<IConfirmationUI> service)
+        : service_(service), callback_{nullptr, nullptr} {}
+    sp<IConfirmationUI> service_;
+
+    // The callback_lock_ protects the callback_ field against concurrent modification.
+    // IMPORTANT: It must never be held while calling the call back.
+    std::mutex callback_lock_;
+    ApcCompatCallback callback_;
+};
+
+}  // namespace keystore2
+
+using namespace keystore2;
+
+ApcCompatServiceHandle tryGetUserConfirmationService() {
+    return reinterpret_cast<ApcCompatServiceHandle>(ConfuiCompatSession::tryGetService());
+}
+
+uint32_t promptUserConfirmation(ApcCompatServiceHandle handle, ApcCompatCallback callback,
+                                const char* prompt_text, const uint8_t* extra_data,
+                                size_t extra_data_size, char const* locale,
+                                ApcCompatUiOptions ui_options) {
+    auto session = reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
+    return (*session)->promptUserConfirmation(callback, prompt_text, extra_data, extra_data_size,
+                                              locale, ui_options);
+}
+
+void abortUserConfirmation(ApcCompatServiceHandle handle) {
+    auto session = reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
+    (*session)->abort();
+}
+
+void closeUserConfirmationService(ApcCompatServiceHandle handle) {
+    // Closing the handle implicitly aborts an ongoing sessions.
+    // Note that a resulting callback is still safely conducted, because we only delete a
+    // StrongPointer below. libhwbinder still owns another StrongPointer to this session.
+    abortUserConfirmation(handle);
+    delete reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
+}
+
+const ApcCompatServiceHandle INVALID_SERVICE_HANDLE = nullptr;
diff --git a/keystore2/apc_compat/apc_compat.hpp b/keystore2/apc_compat/apc_compat.hpp
new file mode 100644
index 0000000..15fa5f4
--- /dev/null
+++ b/keystore2/apc_compat/apc_compat.hpp
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+using ApcCompatServiceHandle = void*;
+
+#define APC_COMPAT_ERROR_OK 0
+#define APC_COMPAT_ERROR_CANCELLED 1
+#define APC_COMPAT_ERROR_ABORTED 2
+#define APC_COMPAT_ERROR_OPERATION_PENDING 3
+#define APC_COMPAT_ERROR_IGNORED 4
+#define APC_COMPAT_ERROR_SYSTEM_ERROR 5
+
+extern "C" {
+
+extern const ApcCompatServiceHandle INVALID_SERVICE_HANDLE;
+
+/**
+ * This struct holds the ui options for the protected confirmation dialog.
+ */
+struct ApcCompatUiOptions {
+    /**
+     * If set to true inverted color mode is used.
+     */
+    bool inverted;
+    /**
+     * If set to true magnified fonts are used.
+     */
+    bool magnified;
+};
+
+/**
+ * Represents a result callback that is called when a confirmation session was successfully
+ * started.
+ * The field `data` is an opaque callback context handle. It must be passed to the `result`
+ * function.
+ *
+ * IMPORTANT: The life cycle of `data` ends when `result` is called. The callback must not
+ *            be called a second time.
+ *
+ * The callback function `result` has the prototype:
+ * void result(
+ *     void* data,
+ *     uint32_t rc,
+ *     const uint8_t* tbs_message,
+ *     size_t tbs_message_size,
+ *     const uint8_t* confirmation_token,
+ *     size_t confirmation_token_size)
+ *
+ * * data - must be the data field of the structure.
+ * * rc - response code, one of:
+ *      * APC_COMPAT_ERROR_OK - The user confirmed the prompt text.
+ *      * APC_COMPAT_ERROR_CANCELLED - The user rejected the prompt text.
+ *      * APC_COMPAT_ERROR_ABORTED - `abortUserConfirmation` was called.
+ *      * APC_COMPAT_ERROR_SYSTEM_ERROR - An unspecified system error occurred.
+ * * tbs_message(_size) - Pointer to and size of the to-be-signed message. Must
+ *      be NULL and 0 respectively if `rc != APC_COMPAT_ERROR_OK`.
+ * * confirmation_token(_size) - Pointer to and size of the confirmation token. Must
+ *      be NULL and 0 respectively if `rc != APC_COMPAT_ERROR_OK`.
+ */
+struct ApcCompatCallback {
+    void* data;
+    void (*result)(void*, uint32_t, const uint8_t*, size_t, const uint8_t*, size_t);
+};
+
+/**
+ * Attempts to make a connection to the confirmationui HIDL backend.
+ * If a valid service handle is returned it stays valid until
+ * `closeUserConfirmationService` is called.
+ *
+ * @return A valid service handle on success or INVALID_SERVICE_HANDLE
+ *         on failure.
+ */
+ApcCompatServiceHandle tryGetUserConfirmationService();
+
+/**
+ * Attempts to start a protected confirmation session on the given service handle.
+ * The function takes ownership of the callback object (`cb`) IFF APC_COMPAT_ERROR_OK
+ * is returned. The resources referenced by the callback object must stay valid
+ * until the callback is called.
+ *
+ * @param handle A valid service handle as returned by `tryGetUserConfirmationService()`.
+ * @cb A ApcCompatCallback structure that represents a callback function with session data.
+ * @param prompt_text A UTF-8 encoded prompt string.
+ * @param extra_data Free form extra data.
+ * @param extra_data_size size of the extra data buffer in bytes.
+ * @param locale A locale string.
+ * @param ui_options A UI options. See ApcCompatUiOptions above.
+ * @retval APC_COMPAT_ERROR_OK on success.
+ * @retval APC_COMPAT_ERROR_OPERATION_PENDING if another operation was already in progress.
+ * @retval APC_COMPAT_ERROR_SYSTEM_ERROR if an unspecified system error occurred.
+ */
+uint32_t promptUserConfirmation(ApcCompatServiceHandle handle, struct ApcCompatCallback cb,
+                                const char* prompt_text, const uint8_t* extra_data,
+                                size_t extra_data_size, char const* locale,
+                                ApcCompatUiOptions ui_options);
+
+/**
+ * Aborts a running confirmation session or no-op if no session is running.
+ * If a session is running this results in a `result` callback with
+ * `rc == APC_COMPAT_ERROR_ABORTED`. Mind though that the callback can still yield other
+ * results even after this function was called, because it may race with an actual user
+ * response. In any case, there will be only one callback response for each session
+ * successfully started with promptUserConfirmation.
+ *
+ * @param handle A valid session handle as returned by `tryGetUserConfirmationService()`
+ */
+void abortUserConfirmation(ApcCompatServiceHandle handle);
+
+/**
+ * Closes a valid service session as returned by `tryGetUserConfirmationService()`.
+ * If a session is still running it is implicitly aborted. In this case, freeing up of the resources
+ * referenced by the service handle is deferred until the callback has completed.
+ *
+ * @param handle A valid session handle as returned by `tryGetUserConfirmationService()`
+ */
+void closeUserConfirmationService(ApcCompatServiceHandle);
+
+}
diff --git a/keystore2/apc_compat/apc_compat.rs b/keystore2/apc_compat/apc_compat.rs
new file mode 100644
index 0000000..4391f5b
--- /dev/null
+++ b/keystore2/apc_compat/apc_compat.rs
@@ -0,0 +1,201 @@
+// Copyright 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.
+
+//! This crate implements a safe wrapper around the ConfirmationUI HIDL spec, which
+//! is the backend for Android Protected Confirmation (APC).
+//!
+//! It provides a safe wrapper around a C++ implementation of ConfirmationUI
+//! client.
+
+use keystore2_apc_compat_bindgen::{
+    abortUserConfirmation, closeUserConfirmationService, promptUserConfirmation, size_t,
+    tryGetUserConfirmationService, ApcCompatCallback, ApcCompatServiceHandle,
+};
+pub use keystore2_apc_compat_bindgen::{
+    ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
+    APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
+    APC_COMPAT_ERROR_SYSTEM_ERROR, INVALID_SERVICE_HANDLE,
+};
+use std::{ffi::CString, slice};
+
+/// Safe wrapper around the ConfirmationUI HIDL spec.
+///
+/// # Example
+/// ```
+/// struct Cb();
+/// impl ApcHalCallback for Cb {
+///     fn result(
+///         &self,
+///         rc: u32,
+///         message: Option<&[u8]>,
+///         token: Option<&[u8]>,
+///     ) {
+///         println!("Callback called with rc: {}, message: {}, token: {}", rc, message, token);
+///     }
+/// };
+///
+/// fn prompt() -> Result<(), u32> {
+///     let hal = ApcHal::try_get_service()?;
+///     hal.prompt_user_confirmation(Box::new(Cb()), "Do you agree?", b"extra data", "en", 0)?;
+/// }
+///
+/// ```
+pub struct ApcHal(ApcCompatServiceHandle);
+
+unsafe impl Send for ApcHal {}
+unsafe impl Sync for ApcHal {}
+
+impl Drop for ApcHal {
+    fn drop(&mut self) {
+        // # Safety:
+        // This ends the life cycle of the contained `ApcCompatServiceHandle` owned by this
+        // `ApcHal` object.
+        //
+        // `ApcHal` objects are only created if a valid handle was acquired so self.0 is
+        // always valid when dropped.
+        unsafe {
+            closeUserConfirmationService(self.0);
+        }
+    }
+}
+
+type Callback = dyn FnOnce(u32, Option<&[u8]>, Option<&[u8]>);
+
+extern "C" fn confirmation_result_callback(
+    handle: *mut ::std::os::raw::c_void,
+    rc: u32,
+    tbs_message: *const u8,
+    tbs_message_size: size_t,
+    confirmation_token: *const u8,
+    confirmation_token_size: size_t,
+) {
+    // # Safety:
+    // The C/C++ implementation must pass to us the handle that was created
+    // and assigned to the `ApcCompatCallback::data` field in
+    // `ApcHal::prompt_user_confirmation` below. Also we consume the handle,
+    // by letting `hal_cb` go out of scope with this function call. So
+    // the C/C++ implementation must assure that each `ApcCompatCallback` is only used once.
+    let hal_cb: Box<Box<Callback>> = unsafe { Box::from_raw(handle as *mut Box<Callback>) };
+    let tbs_message = match (tbs_message.is_null(), tbs_message_size) {
+        (true, _) | (_, 0) => None,
+        (false, s) => Some(
+            // # Safety:
+            // If the pointer and size is not nullptr and not 0 respectively, the C/C++
+            // implementation must pass a valid pointer to an allocation of at least size bytes,
+            // and the pointer must be valid until this function returns.
+            unsafe { slice::from_raw_parts(tbs_message, s as usize) },
+        ),
+    };
+    let confirmation_token = match (confirmation_token.is_null(), confirmation_token_size) {
+        (true, _) | (_, 0) => None,
+        (false, s) => Some(
+            // # Safety:
+            // If the pointer and size is not nullptr and not 0 respectively, the C/C++
+            // implementation must pass a valid pointer to an allocation of at least size bytes,
+            // and the pointer must be valid until this function returns.
+            unsafe { slice::from_raw_parts(confirmation_token, s as usize) },
+        ),
+    };
+    hal_cb(rc, tbs_message, confirmation_token)
+}
+
+impl ApcHal {
+    /// Attempts to connect to the APC (confirmationui) backend. On success, it returns an
+    /// initialized `ApcHal` object.
+    pub fn try_get_service() -> Option<Self> {
+        // # Safety:
+        // `tryGetUserConfirmationService` returns a valid handle or INVALID_SERVICE_HANDLE.
+        // On success, `ApcHal` takes ownership of this handle and frees it with
+        // `closeUserConfirmationService` when dropped.
+        let handle = unsafe { tryGetUserConfirmationService() };
+        match handle {
+            h if h == unsafe { INVALID_SERVICE_HANDLE } => None,
+            h => Some(Self(h)),
+        }
+    }
+
+    /// Attempts to start a confirmation prompt. The given callback is consumed, and it is
+    /// guaranteed to be called eventually IFF this function returns `APC_COMPAT_ERROR_OK`.
+    ///
+    /// The callback has the following arguments:
+    /// rc: u32 - The reason for the termination which takes one of the values.
+    ///       * `APC_COMPAT_ERROR_OK` - The user confirmed the prompted message.
+    ///       * `APC_COMPAT_ERROR_CANCELLED` - The user rejected the prompted message.
+    ///       * `APC_COMPAT_ERROR_ABORTED` - The prompt was aborted either because the client
+    ///          aborted. the session or an asynchronous system event occurred that ended the
+    ///          prompt prematurely.
+    ///       * `APC_COMPAT_ERROR_SYSTEMERROR` - An unspecified system error occurred. Logs may
+    ///          have more information.
+    ///
+    /// data_confirmed: Option<&[u8]> and
+    /// confirmation_token: Option<&[u8]> hold the confirmed message and the confirmation token
+    /// respectively. They must be `Some()` if `rc == APC_COMPAT_ERROR_OK` and `None` otherwise.
+    pub fn prompt_user_confirmation<F>(
+        &self,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_opts: ApcCompatUiOptions,
+        cb: F,
+    ) -> Result<(), u32>
+    where
+        F: FnOnce(u32, Option<&[u8]>, Option<&[u8]>),
+    {
+        let cb_data_ptr = Box::into_raw(Box::new(Box::new(cb)));
+        let cb = ApcCompatCallback {
+            data: cb_data_ptr as *mut std::ffi::c_void,
+            result: Some(confirmation_result_callback),
+        };
+        let prompt_text = CString::new(prompt_text).unwrap();
+        let locale = CString::new(locale).unwrap();
+        // # Safety:
+        // The `ApcCompatCallback` object (`cb`) is passed to the callee by value, and with it
+        // ownership of the `data` field pointer. The data pointer is guaranteed to be valid
+        // until the C/C++ implementation calls the callback. Calling the callback consumes
+        // the data pointer. The C/C++ implementation must not access it after calling the
+        // callback and it must not call the callback a second time.
+        //
+        // The C/C++ must make no assumptions about the life time of the other parameters after
+        // the function returns.
+        let rc = unsafe {
+            promptUserConfirmation(
+                self.0,
+                cb,
+                prompt_text.as_ptr(),
+                extra_data.as_ptr(),
+                extra_data.len() as size_t,
+                locale.as_ptr(),
+                ui_opts,
+            )
+        };
+        match rc {
+            APC_COMPAT_ERROR_OK => Ok(()),
+            rc => {
+                // # Safety:
+                // If promptUserConfirmation does not succeed, it must not take ownership of the
+                // callback, so we must destroy it.
+                unsafe { Box::from_raw(cb_data_ptr) };
+                Err(rc)
+            }
+        }
+    }
+
+    /// Aborts a running confirmation session, or no-op if none is running.
+    pub fn abort(&self) {
+        // # Safety:
+        // It is always safe to call `abortUserConfirmation`, because spurious calls are ignored.
+        // The handle argument must be valid, but this is an invariant of `ApcHal`.
+        unsafe { abortUserConfirmation(self.0) }
+    }
+}
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
new file mode 100644
index 0000000..105e071
--- /dev/null
+++ b/keystore2/src/apc.rs
@@ -0,0 +1,366 @@
+// Copyright 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.
+
+// TODO The confirmation token is yet unused.
+#![allow(unused_variables)]
+
+//! This module implements the Android Protected Confirmation (APC) service as defined
+//! in the android.security.apc AIDL spec.
+
+use std::{
+    cmp::PartialEq,
+    collections::HashMap,
+    sync::{Arc, Mutex},
+};
+
+use crate::utils::{compat_2_response_code, ui_opts_2_compat};
+use android_security_apc::aidl::android::security::apc::{
+    IConfirmationCallback::IConfirmationCallback,
+    IProtectedConfirmation::{BnProtectedConfirmation, IProtectedConfirmation},
+    ResponseCode::ResponseCode,
+};
+use android_security_apc::binder::{
+    ExceptionCode, Interface, Result as BinderResult, SpIBinder, Status as BinderStatus,
+};
+use anyhow::{Context, Result};
+use binder::{IBinder, ThreadState};
+use keystore2_apc_compat::ApcHal;
+use keystore2_selinux as selinux;
+use std::time::{Duration, Instant};
+
+/// This is the main APC error type, it wraps binder exceptions and the
+/// APC ResponseCode.
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+    /// Wraps an Android Protected Confirmation (APC) response code as defined by the
+    /// android.security.apc AIDL interface specification.
+    #[error("Error::Rc({0:?})")]
+    Rc(ResponseCode),
+    /// Wraps a Binder exception code other than a service specific exception.
+    #[error("Binder exception code {0:?}, {1:?}")]
+    Binder(ExceptionCode, i32),
+}
+
+impl Error {
+    /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
+    pub fn sys() -> Self {
+        Error::Rc(ResponseCode::SYSTEM_ERROR)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::OPERATION_PENDING)`
+    pub fn pending() -> Self {
+        Error::Rc(ResponseCode::OPERATION_PENDING)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::CANCELLED)`
+    pub fn cancelled() -> Self {
+        Error::Rc(ResponseCode::CANCELLED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::ABORTED)`
+    pub fn aborted() -> Self {
+        Error::Rc(ResponseCode::ABORTED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::IGNORED)`
+    pub fn ignored() -> Self {
+        Error::Rc(ResponseCode::IGNORED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::UNIMPLEMENTED)`
+    pub fn unimplemented() -> Self {
+        Error::Rc(ResponseCode::UNIMPLEMENTED)
+    }
+}
+
+/// This function should be used by confirmation service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// `Error::Rc(x)` variants get mapped onto a service specific error code of `x`.
+/// `selinux::Error::perm()` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions get mapped onto ResponseCode::SYSTEM_ERROR`.
+///
+/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
+/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
+/// typically returns Ok(value).
+pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
+where
+    F: FnOnce(U) -> BinderResult<T>,
+{
+    result.map_or_else(
+        |e| {
+            log::error!("{:#?}", e);
+            let root_cause = e.root_cause();
+            let rc = match root_cause.downcast_ref::<Error>() {
+                Some(Error::Rc(rcode)) => rcode.0,
+                Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+                None => match root_cause.downcast_ref::<selinux::Error>() {
+                    Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+                    _ => ResponseCode::SYSTEM_ERROR.0,
+                },
+            };
+            Err(BinderStatus::new_service_specific_error(rc, None))
+        },
+        handle_ok,
+    )
+}
+
+/// Rate info records how many failed attempts a client has made to display a protected
+/// confirmation prompt. Clients are penalized for attempts that get declined by the user
+/// or attempts that get aborted by the client itself.
+///
+/// After the third failed attempt the client has to cool down for 30 seconds before it
+/// it can retry. After the sixth failed attempt, the time doubles with every failed attempt
+/// until it goes into saturation at 24h.
+///
+/// A successful user prompt resets the counter.
+#[derive(Debug, Clone)]
+struct RateInfo {
+    counter: u32,
+    timestamp: Instant,
+}
+
+impl RateInfo {
+    const ONE_DAY: Duration = Duration::from_secs(60u64 * 60u64 * 24u64);
+
+    fn get_remaining_back_off(&self) -> Option<Duration> {
+        let back_off = match self.counter {
+            // The first three attempts come without penalty.
+            0..=2 => return None,
+            // The next three attempts are are penalized with 30 seconds back off time.
+            3..=5 => Duration::from_secs(30),
+            // After that we double the back off time the with every additional attempt
+            // until we reach 1024m (~17h).
+            6..=16 => Duration::from_secs(60)
+                .checked_mul(1u32 << (self.counter - 6))
+                .unwrap_or(Self::ONE_DAY),
+            // After that we cap of at 24h between attempts.
+            _ => Self::ONE_DAY,
+        };
+        let elapsed = self.timestamp.elapsed();
+        // This does exactly what we want.
+        // `back_off - elapsed` is the remaining back off duration or None if elapsed is larger
+        // than back_off. Also, this operation cannot overflow as long as elapsed is less than
+        // back_off, which is all that we care about.
+        back_off.checked_sub(elapsed)
+    }
+}
+
+impl Default for RateInfo {
+    fn default() -> Self {
+        Self { counter: 0u32, timestamp: Instant::now() }
+    }
+}
+
+/// The APC session state represents the state of an APC session.
+struct ApcSessionState {
+    /// A reference to the APC HAL backend.
+    hal: Arc<ApcHal>,
+    /// The client callback object.
+    cb: SpIBinder,
+    /// The uid of the owner of this APC session.
+    uid: u32,
+    /// The time when this session was started.
+    start: Instant,
+    /// This is set when the client calls abort.
+    /// This is used by the rate limiting logic to determine
+    /// if the client needs to be penalized for this attempt.
+    client_aborted: bool,
+}
+
+#[derive(Default)]
+struct ApcState {
+    session: Option<ApcSessionState>,
+    rate_limiting: HashMap<u32, RateInfo>,
+}
+
+/// Implementation of the APC service.
+pub struct ApcManager {
+    state: Arc<Mutex<ApcState>>,
+}
+
+impl Interface for ApcManager {}
+
+impl ApcManager {
+    /// Create a new instance of the Android Protected Confirmation service.
+    pub fn new_native_binder() -> Result<impl IProtectedConfirmation> {
+        let result = BnProtectedConfirmation::new_binder(Self {
+            state: Arc::new(Mutex::new(Default::default())),
+        });
+        result.as_binder().set_requesting_sid(true);
+        Ok(result)
+    }
+
+    fn result(
+        state: Arc<Mutex<ApcState>>,
+        rc: u32,
+        data_confirmed: Option<&[u8]>,
+        confirmation_token: Option<&[u8]>,
+    ) {
+        let mut state = state.lock().unwrap();
+        let (callback, uid, start, client_aborted) = match state.session.take() {
+            None => return, // Nothing to do
+            Some(ApcSessionState { cb: callback, uid, start, client_aborted, .. }) => {
+                (callback, uid, start, client_aborted)
+            }
+        };
+
+        let rc = compat_2_response_code(rc);
+
+        // Update rate limiting information.
+        match (rc, client_aborted) {
+            // If the user confirmed the dialog.
+            (ResponseCode::OK, _) => {
+                // Reset counter.
+                state.rate_limiting.remove(&uid);
+                // TODO at this point we need to send the confirmation token to where keystore can
+                // use it.
+            }
+            // If cancelled by the user or if aborted by the client.
+            (ResponseCode::CANCELLED, _) | (ResponseCode::ABORTED, true) => {
+                // Penalize.
+                let mut rate_info = state.rate_limiting.entry(uid).or_default();
+                rate_info.counter += 1;
+                rate_info.timestamp = start;
+            }
+            // In any other case this try does not count at all.
+            _ => {}
+        }
+        drop(state);
+
+        if let Ok(listener) = callback.into_interface::<dyn IConfirmationCallback>() {
+            if let Err(e) = listener.onCompleted(rc, data_confirmed) {
+                log::error!(
+                    "In ApcManagerCallback::result: Reporting completion to client failed {:?}",
+                    e
+                )
+            }
+        } else {
+            log::error!("In ApcManagerCallback::result: SpIBinder is not a IConfirmationCallback.");
+        }
+    }
+
+    fn present_prompt(
+        &self,
+        listener: &dyn IConfirmationCallback,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_option_flags: i32,
+    ) -> Result<()> {
+        let mut state = self.state.lock().unwrap();
+        if state.session.is_some() {
+            return Err(Error::pending())
+                .context("In ApcManager::present_prompt: Session pending.");
+        }
+
+        // Perform rate limiting.
+        let uid = ThreadState::get_calling_uid();
+        match state.rate_limiting.get(&uid) {
+            None => {}
+            Some(rate_info) => {
+                if let Some(back_off) = rate_info.get_remaining_back_off() {
+                    return Err(Error::sys()).context(format!(
+                        "In ApcManager::present_prompt: Cooling down. Remaining back-off: {}s",
+                        back_off.as_secs()
+                    ));
+                }
+            }
+        }
+
+        let hal = ApcHal::try_get_service();
+        let hal = match hal {
+            None => {
+                return Err(Error::unimplemented())
+                    .context("In ApcManager::present_prompt: APC not supported.")
+            }
+            Some(h) => Arc::new(h),
+        };
+
+        let ui_opts = ui_opts_2_compat(ui_option_flags);
+
+        let state_clone = self.state.clone();
+        hal.prompt_user_confirmation(
+            prompt_text,
+            extra_data,
+            locale,
+            ui_opts,
+            |rc, data_confirmed, confirmation_token| {
+                Self::result(state_clone, rc, data_confirmed, confirmation_token)
+            },
+        )
+        .map_err(|rc| Error::Rc(compat_2_response_code(rc)))
+        .context("In present_prompt: Failed to present prompt.")?;
+        state.session = Some(ApcSessionState {
+            hal,
+            cb: listener.as_binder(),
+            uid,
+            start: Instant::now(),
+            client_aborted: false,
+        });
+        Ok(())
+    }
+
+    fn cancel_prompt(&self, listener: &dyn IConfirmationCallback) -> Result<()> {
+        let mut state = self.state.lock().unwrap();
+        let hal = match &mut state.session {
+            None => {
+                return Err(Error::ignored())
+                    .context("In cancel_prompt: Attempt to cancel non existing session. Ignoring.")
+            }
+            Some(session) => {
+                if session.cb != listener.as_binder() {
+                    return Err(Error::ignored()).context(concat!(
+                        "In cancel_prompt: Attempt to cancel session not belonging to caller. ",
+                        "Ignoring."
+                    ));
+                }
+                session.client_aborted = true;
+                session.hal.clone()
+            }
+        };
+        drop(state);
+        hal.abort();
+        Ok(())
+    }
+
+    fn is_supported() -> Result<bool> {
+        Ok(ApcHal::try_get_service().is_some())
+    }
+}
+
+impl IProtectedConfirmation for ApcManager {
+    fn presentPrompt(
+        &self,
+        listener: &dyn IConfirmationCallback,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_option_flags: i32,
+    ) -> BinderResult<()> {
+        map_or_log_err(
+            self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags),
+            Ok,
+        )
+    }
+    fn cancelPrompt(&self, listener: &dyn IConfirmationCallback) -> BinderResult<()> {
+        map_or_log_err(self.cancel_prompt(listener), Ok)
+    }
+    fn isSupported(&self) -> BinderResult<bool> {
+        map_or_log_err(Self::is_supported(), Ok)
+    }
+}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 2916549..59e5972 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -15,11 +15,13 @@
 //! This crate implements the Keystore 2.0 service entry point.
 
 use binder::Interface;
+use keystore2::apc::ApcManager;
 use keystore2::service::KeystoreService;
 use log::{error, info};
 use std::panic;
 
 static KS2_SERVICE_NAME: &str = "android.system.keystore2";
+static APC_SERVICE_NAME: &str = "android.security.apc";
 
 /// Keystore 2.0 takes one argument which is a path indicating its designated working directory.
 fn main() {
@@ -55,6 +57,13 @@
         panic!("Failed to register service {} because of {:?}.", KS2_SERVICE_NAME, e);
     });
 
+    let apc_service = ApcManager::new_native_binder().unwrap_or_else(|e| {
+        panic!("Failed to create service {} because of {:?}.", APC_SERVICE_NAME, e);
+    });
+    binder::add_service(APC_SERVICE_NAME, apc_service.as_binder()).unwrap_or_else(|e| {
+        panic!("Failed to register service {} because of {:?}.", APC_SERVICE_NAME, e);
+    });
+
     info!("Successfully registered Keystore 2.0 service.");
 
     info!("Starting thread pool now.");
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 5ad77d1..3fb938c 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -14,6 +14,7 @@
 
 //! This crate implements the Android Keystore 2.0 service.
 
+pub mod apc;
 pub mod auth_token_handler;
 pub mod database;
 pub mod enforcements;
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index d50f70e..eab9b4d 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -25,11 +25,20 @@
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     KeyCharacteristics::KeyCharacteristics, SecurityLevel::SecurityLevel,
 };
+use android_security_apc::aidl::android::security::apc::{
+    IProtectedConfirmation::{FLAG_UI_OPTION_INVERTED, FLAG_UI_OPTION_MAGNIFIED},
+    ResponseCode::ResponseCode as ApcResponseCode,
+};
 use android_system_keystore2::aidl::android::system::keystore2::{
     Authorization::Authorization, KeyDescriptor::KeyDescriptor,
 };
 use anyhow::{anyhow, Context};
 use binder::{FromIBinder, SpIBinder, ThreadState};
+use keystore2_apc_compat::{
+    ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
+    APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
+    APC_COMPAT_ERROR_SYSTEM_ERROR,
+};
 use std::convert::TryFrom;
 use std::sync::Mutex;
 
@@ -146,6 +155,31 @@
     i64::try_from(current_time.tv_sec).unwrap()
 }
 
+/// Converts a response code as returned by the Android Protected Confirmation HIDL compatibility
+/// module (keystore2_apc_compat) into a ResponseCode as defined by the APC AIDL
+/// (android.security.apc) spec.
+pub fn compat_2_response_code(rc: u32) -> ApcResponseCode {
+    match rc {
+        APC_COMPAT_ERROR_OK => ApcResponseCode::OK,
+        APC_COMPAT_ERROR_CANCELLED => ApcResponseCode::CANCELLED,
+        APC_COMPAT_ERROR_ABORTED => ApcResponseCode::ABORTED,
+        APC_COMPAT_ERROR_OPERATION_PENDING => ApcResponseCode::OPERATION_PENDING,
+        APC_COMPAT_ERROR_IGNORED => ApcResponseCode::IGNORED,
+        APC_COMPAT_ERROR_SYSTEM_ERROR => ApcResponseCode::SYSTEM_ERROR,
+        _ => ApcResponseCode::SYSTEM_ERROR,
+    }
+}
+
+/// Converts the UI Options flags as defined by the APC AIDL (android.security.apc) spec into
+/// UI Options flags as defined by the Android Protected Confirmation HIDL compatibility
+/// module (keystore2_apc_compat).
+pub fn ui_opts_2_compat(opt: i32) -> ApcCompatUiOptions {
+    ApcCompatUiOptions {
+        inverted: (opt & FLAG_UI_OPTION_INVERTED) != 0,
+        magnified: (opt & FLAG_UI_OPTION_MAGNIFIED) != 0,
+    }
+}
+
 /// AID offset for uid space partitioning.
 /// TODO: Replace with bindgen generated from libcutils. b/175619259
 pub const AID_USER_OFFSET: u32 = 100000;