Create AuthenticationOutcomeReceiver

OutcomeReceiver is only available starting from SDK 31.
Since the target SDK is 29, create a new AuthenticationOutcomeReceiver
that can be used instead.

Test: internal tests
Bug: 322048605
Change-Id: Id08c6d9e74489cc9f6ab0e681bc64525f86ac72e
diff --git a/ts43authentication/src/com/android/libraries/ts43authentication/AuthenticationOutcomeReceiver.java b/ts43authentication/src/com/android/libraries/ts43authentication/AuthenticationOutcomeReceiver.java
new file mode 100644
index 0000000..37ad354
--- /dev/null
+++ b/ts43authentication/src/com/android/libraries/ts43authentication/AuthenticationOutcomeReceiver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 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.libraries.ts43authentication;
+
+/**
+ * Callback interface intended for use when an TS.43 authentication request may result in a failure.
+ * This interface may be used in cases where an asynchronous API may complete either with a value
+ * or with a {@link Throwable} that indicates an error.
+ * Functionally the same as an OutcomeReceiver, but creating a new class for backwards compatibility
+ * on devices with SDK < 31.
+ *
+ * @param <R> The type of the result that's being sent.
+ * @param <E> The type of the {@link Throwable} that contains more information about the error.
+ */
+public interface AuthenticationOutcomeReceiver <R, E extends Throwable> {
+    /**
+     * Called when the asynchronous operation succeeds and delivers a result value.
+     *
+     * @param result The value delivered by the asynchronous operation.
+     */
+    void onResult(R result);
+
+    /**
+     * Called when the asynchronous operation fails. The mode of failure is indicated by the
+     * {@link Throwable} passed as an argument to this method.
+     *
+     * @param error A subclass of {@link Throwable} with more details about the error that occurred.
+     */
+    default void onError(E error) {}
+}
diff --git a/ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java b/ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java
index 799a3a3..da45671 100644
--- a/ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java
+++ b/ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java
@@ -26,7 +26,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.OutcomeReceiver;
 import android.os.PersistableBundle;
 import android.telephony.SubscriptionInfo;
 import android.util.Log;
@@ -131,12 +130,12 @@
         @Nullable private final String mEntitlementVersion;
         private final String mAppId;
         private final Executor mExecutor;
-        private final OutcomeReceiver<
+        private final AuthenticationOutcomeReceiver<
                 Ts43Authentication.Ts43AuthToken, AuthenticationException> mCallback;
 
         private EapAkaAuthenticationRequest(String appName, @Nullable String appVersion,
                 int slotIndex, URL entitlementServerAddress, @Nullable String entitlementVersion,
-                String appId, Executor executor, OutcomeReceiver<
+                String appId, Executor executor, AuthenticationOutcomeReceiver<
                         Ts43Authentication.Ts43AuthToken, AuthenticationException> callback) {
             mAppName = appName;
             mAppVersion = appVersion;
@@ -157,12 +156,12 @@
         @Nullable private final String mEntitlementVersion;
         private final String mAppId;
         private final Executor mExecutor;
-        private final OutcomeReceiver<URL, AuthenticationException> mCallback;
+        private final AuthenticationOutcomeReceiver<URL, AuthenticationException> mCallback;
 
         private OidcAuthenticationServerRequest(String appName, @Nullable String appVersion,
                 int slotIndex, URL entitlementServerAddress, @Nullable String entitlementVersion,
                 String appId, Executor executor,
-                OutcomeReceiver<URL, AuthenticationException> callback) {
+                AuthenticationOutcomeReceiver<URL, AuthenticationException> callback) {
             mAppName = appName;
             mAppVersion = appVersion;
             mSlotIndex = slotIndex;
@@ -179,11 +178,12 @@
         @Nullable private final String mEntitlementVersion;
         private final URL mAesUrl;
         private final Executor mExecutor;
-        private final OutcomeReceiver<
+        private final AuthenticationOutcomeReceiver<
                 Ts43Authentication.Ts43AuthToken, AuthenticationException> mCallback;
 
         private OidcAuthenticationRequest(URL entitlementServerAddress,
-                @Nullable String entitlementVersion, URL aesUrl, Executor executor, OutcomeReceiver<
+                @Nullable String entitlementVersion, URL aesUrl, Executor executor,
+                AuthenticationOutcomeReceiver<
                         Ts43Authentication.Ts43AuthToken, AuthenticationException> callback) {
             mEntitlementServerAddress = entitlementServerAddress;
             mEntitlementVersion = entitlementVersion;
@@ -216,14 +216,17 @@
      *        Refer to GSMA Service Entitlement Configuration section 2.3.
      * @param executor The executor on which the callback will be called.
      * @param callback The callback to receive the results of the authentication request.
-     *        If authentication is successful, {@link OutcomeReceiver#onResult(Object)} will return
-     *        a {@link Ts43Authentication.Ts43AuthToken} with the token and validity.
-     *        If the authentication fails, {@link OutcomeReceiver#onError(Throwable)} will return an
+     *        If authentication is successful,
+     *        {@link AuthenticationOutcomeReceiver#onResult(Object)} will return a
+     *        {@link Ts43Authentication.Ts43AuthToken} with the token and validity.
+     *        If the authentication fails,
+     *        {@link AuthenticationOutcomeReceiver#onError(Throwable)} will return an
      *        {@link AuthenticationException} with the failure details.
      */
     public void requestEapAkaAuthentication(PersistableBundle configs, String packageName,
             @Nullable String appVersion, int slotIndex, URL entitlementServerAddress,
-            @Nullable String entitlementVersion, String appId, Executor executor, OutcomeReceiver<
+            @Nullable String entitlementVersion, String appId, Executor executor,
+            AuthenticationOutcomeReceiver<
                     Ts43Authentication.Ts43AuthToken, AuthenticationException> callback) {
         String[] allowedPackageInfo = configs.getStringArray(KEY_ALLOWED_CERTIFICATES_STRING_ARRAY);
         String certificate = getMatchingCertificate(allowedPackageInfo, packageName);
@@ -245,8 +248,8 @@
      * The client should present the content of the URL to the user to continue the authentication
      * process. After receiving a response from the authentication server, the client can call
      * {@link #requestOidcAuthentication(
-     * PersistableBundle, String, URL, String, URL, Executor, OutcomeReceiver)} to get the
-     * authentication token.
+     * PersistableBundle, String, URL, String, URL, Executor, AuthenticationOutcomeReceiver)} to get
+     * the authentication token.
      *
      * @param configs The configurations that should be applied to this authentication request.
      *        The keys of the bundle must be in {@link ConfigurationKey}.
@@ -267,18 +270,18 @@
      *        Refer to GSMA Service Entitlement Configuration section 2.3.
      * @param executor The executor on which the callback will be called.
      * @param callback The callback to receive the results of the authentication server request.
-     *        If the request is successful, {@link OutcomeReceiver#onResult(Object)} will return a
-     *        {@link URL} with all the required parameters for the client to launch a user interface
-     *        for users to complete the authentication process. The parameters in URL include
-     *        {@code client_id}, {@code redirect_uri}, {@code state}, and {@code nonce}.
-     *        If the authentication fails, {@link OutcomeReceiver#onError(Throwable)} will return an
-     *        {@link AuthenticationException} with the failure details.
+     *        If the request is successful, {@link AuthenticationOutcomeReceiver#onResult(Object)}
+     *        will return a {@link URL} with all the required parameters for the client to launch a
+     *        user interface for users to complete the authentication process. The parameters in URL
+     *        include {@code client_id}, {@code redirect_uri}, {@code state}, and {@code nonce}.
+     *        If the authentication fails, {@link AuthenticationOutcomeReceiver#onError(Throwable)}
+     *        will return an {@link AuthenticationException} with the failure details.
      */
     public void requestOidcAuthenticationServer(PersistableBundle configs,
             String packageName, @Nullable String appVersion, int slotIndex,
             URL entitlementServerAddress, @Nullable String entitlementVersion,
             String appId, Executor executor,
-            OutcomeReceiver<URL, AuthenticationException> callback) {
+            AuthenticationOutcomeReceiver<URL, AuthenticationException> callback) {
         String[] allowedPackageInfo = configs.getStringArray(KEY_ALLOWED_CERTIFICATES_STRING_ARRAY);
         String certificate = getMatchingCertificate(allowedPackageInfo, packageName);
         if (isCallingPackageAllowed(allowedPackageInfo, packageName, certificate)) {
@@ -309,15 +312,16 @@
      *        URL include the OIDC authentication code {@code code} and {@code state}.
      * @param executor The executor on which the callback will be called.
      * @param callback The callback to receive the results of the authentication request.
-     *        If authentication is successful, {@link OutcomeReceiver#onResult(Object)} will return
-     *        a {@link Ts43Authentication.Ts43AuthToken} with the token and validity.
-     *        If the authentication fails, {@link OutcomeReceiver#onError(Throwable)} will return an
-     *        {@link AuthenticationException} with the failure details.
+     *        If authentication is successful,
+     *        {@link AuthenticationOutcomeReceiver#onResult(Object)} will return a
+     *        {@link Ts43Authentication.Ts43AuthToken} with the token and validity.
+     *        If the authentication fails, {@link AuthenticationOutcomeReceiver#onError(Throwable)}
+     *        will return an {@link AuthenticationException} with the failure details.
      */
     public void requestOidcAuthentication(PersistableBundle configs,
             String packageName, URL entitlementServerAddress,
             @Nullable String entitlementVersion, URL aesUrl, Executor executor,
-            OutcomeReceiver<
+            AuthenticationOutcomeReceiver<
                     Ts43Authentication.Ts43AuthToken, AuthenticationException> callback) {
         String[] allowedPackageInfo = configs.getStringArray(KEY_ALLOWED_CERTIFICATES_STRING_ARRAY);
         String certificate = getMatchingCertificate(allowedPackageInfo, packageName);
@@ -445,8 +449,10 @@
         return signatures;
     }
 
-    private byte[] hexStringToBytes(@Nullable String hex) {
-        if (hex == null) return null;
+    @Nullable private byte[] hexStringToBytes(@Nullable String hex) {
+        if (hex == null) {
+            return null;
+        }
 
         int length = hex.length();
         byte[] ret = new byte[length / 2];