Merge "Support specifying accept configuration document type XML or JSON" into sc-dev
diff --git a/java/com/android/libraries/entitlement/EapAkaHelper.java b/java/com/android/libraries/entitlement/EapAkaHelper.java
index 05c3964..3db48e1 100644
--- a/java/com/android/libraries/entitlement/EapAkaHelper.java
+++ b/java/com/android/libraries/entitlement/EapAkaHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.libraries.entitlement;
 
+import static com.android.libraries.entitlement.eapaka.EapAkaResponse.respondToEapAkaChallenge;
+
 import android.content.Context;
 import android.telephony.TelephonyManager;
 
@@ -23,7 +25,6 @@
 
 import com.android.libraries.entitlement.eapaka.EapAkaApi;
 import com.android.libraries.entitlement.eapaka.EapAkaChallenge;
-import com.android.libraries.entitlement.eapaka.EapAkaResponse;
 
 /**
  * Some utility methods used in EAP-AKA authentication in service entitlement, and could be
@@ -73,17 +74,69 @@
      *
      * <p>Both the challange and response are base-64 encoded EAP-AKA message: refer to
      * RFC 4187 Section 8.1 Message Format/RFC 3748 Session 4 EAP Packet Format.
+     *
+     * @deprecated use {@link getEapAkaResponse(String)} which additionally supports
+     * Synchronization-Failure case.
      */
+    @Deprecated
     @Nullable
     public String getEapAkaChallengeResponse(String challenge) {
+        EapAkaResponse eapAkaResponse = getEapAkaResponse(challenge);
+        return (eapAkaResponse == null)
+                ? null
+                : eapAkaResponse.response(); // Would be null on synchrinization failure
+    }
+
+    /**
+     * Returns the {@link EapAkaResponse} to the given EAP-AKA {@code challenge}, or
+     * {@code null} if failed.
+     *
+     * <p>Both the challange and response are base-64 encoded EAP-AKA message: refer to
+     * RFC 4187 Section 8.1 Message Format/RFC 3748 Session 4 EAP Packet Format.
+     */
+    @Nullable
+    public EapAkaResponse getEapAkaResponse(String challenge) {
         try {
             EapAkaChallenge eapAkaChallenge = EapAkaChallenge.parseEapAkaChallenge(challenge);
-            EapAkaResponse response =
-                    EapAkaResponse.respondToEapAkaChallenge(
-                            mContext, mSimSubscriptionId, eapAkaChallenge);
-            return response.response(); // Would be null on synchrinization failure
+            com.android.libraries.entitlement.eapaka.EapAkaResponse eapAkaResponse =
+                    respondToEapAkaChallenge(mContext, mSimSubscriptionId, eapAkaChallenge);
+            return new EapAkaResponse(
+                    eapAkaResponse.response(), eapAkaResponse.synchronizationFailureResponse());
         } catch (ServiceEntitlementException e) {
             return null;
         }
     }
+
+    // Similar to .eapaka.EapAkaResponse but with simplfied API surface for external usage.
+    /** EAP-AKA response */
+    public static class EapAkaResponse {
+        // RFC 4187 Section 9.4 EAP-Response/AKA-Challenge
+        @Nullable private final String mResponse;
+        // RFC 4187 Section 9.6 EAP-Response/AKA-Synchronization-Failure
+        @Nullable private final String mSynchronizationFailureResponse;
+
+        private EapAkaResponse(
+                @Nullable String response, @Nullable String synchronizationFailureResponse) {
+            mResponse = response;
+            mSynchronizationFailureResponse = synchronizationFailureResponse;
+        }
+
+        /**
+         * Returns EAP-Response/AKA-Challenge, if authentication success.
+         * Otherwise {@code null}.
+         */
+        @Nullable
+        public String response() {
+            return mResponse;
+        }
+
+        /**
+         * Returns EAP-Response/AKA-Synchronization-Failure, if synchronization failure detected.
+         * Otherwise {@code null}.
+         */
+        @Nullable
+        public String synchronizationFailureResponse() {
+            return mSynchronizationFailureResponse;
+        }
+    }
 }
diff --git a/tests/src/com/android/libraries/entitlement/EapAkaHelperTest.java b/tests/src/com/android/libraries/entitlement/EapAkaHelperTest.java
index cb6c4c9..1854bb3 100644
--- a/tests/src/com/android/libraries/entitlement/EapAkaHelperTest.java
+++ b/tests/src/com/android/libraries/entitlement/EapAkaHelperTest.java
@@ -123,4 +123,61 @@
 
         assertThat(response).isNull();
     }
+
+    @Test
+    public void getEapAkaResponse() {
+        when(mMockTelephonyManagerForSubId.getSubscriberId()).thenReturn("234107813240779");
+        when(mMockTelephonyManagerForSubId.getSimOperator()).thenReturn("23410");
+        when(mMockTelephonyManagerForSubId.getIccAuthentication(
+                    TelephonyManager.APPTYPE_USIM, TelephonyManager.AUTHTYPE_EAP_AKA,
+                    "EFzmUpAo8crIBONxlqhyb4EQa4pFhq3pAACzVOE8Zggz6A=="))
+                .thenReturn(
+                        "2wjHnwKln8mjjxDzMKJvLBzMHtm0X9SNBsUWEAbEiAdD7xeqqZ7nsXzukRkIhd6SDZ4bj7s=");
+        String challenge = "AQIAfBcBAAABBQAAXOZSkCjxysgE4"
+                + "3GWqHJvgQIFAABrikWGrekAALNU4TxmCDPoCwUAAJT0nqXeAYlqzT0UGXINENWBBQAA7z3fhImk"
+                + "q+vcCKWIZBdvuYIJAAAPRUFp7KWFo+Thr78Qj9hEkB2zA0i6KakODsufBC+BJQ==";
+        String expectedResponse = "AgIAKBcBAAADAwBAx58CpZ/Jo48LBQAAHBQb3d6L7y5QL7Z5OAjefA==";
+
+        EapAkaHelper.EapAkaResponse response = mEapAkaHelper.getEapAkaResponse(challenge);
+
+        assertThat(response.response()).isEqualTo(expectedResponse);
+        assertThat(response.synchronizationFailureResponse()).isNull();
+    }
+
+    @Test
+    public void getEapAkaResponse_synchronizationFailure() {
+        when(mMockTelephonyManagerForSubId.getSubscriberId()).thenReturn("234107813240779");
+        when(mMockTelephonyManagerForSubId.getSimOperator()).thenReturn("23410");
+        when(mMockTelephonyManagerForSubId.getIccAuthentication(
+                    TelephonyManager.APPTYPE_USIM, TelephonyManager.AUTHTYPE_EAP_AKA,
+                    "EFzmUpAo8crIBONxlqhyb4EQa4pFhq3pAACzVOE8Zggz6A=="))
+                .thenReturn(
+                        "3A4z1Y05YZwhM2fjdhFTFQ==");
+        String challenge = "AQIAfBcBAAABBQAAXOZSkCjxysgE4"
+                + "3GWqHJvgQIFAABrikWGrekAALNU4TxmCDPoCwUAAJT0nqXeAYlqzT0UGXINENWBBQAA7z3fhImk"
+                + "q+vcCKWIZBdvuYIJAAAPRUFp7KWFo+Thr78Qj9hEkB2zA0i6KakODsufBC+BJQ==";
+        String expectedResponse = "AgIAGBcEAAAEBDPVjTlhnCEzZ+N2EVMV";
+
+        EapAkaHelper.EapAkaResponse response = mEapAkaHelper.getEapAkaResponse(challenge);
+
+        assertThat(response.response()).isNull();
+        assertThat(response.synchronizationFailureResponse()).isEqualTo(expectedResponse);
+    }
+
+    @Test
+    public void getEapAkaResponse_getIccAuthenticationFailed() {
+        when(mMockTelephonyManagerForSubId.getSubscriberId()).thenReturn("234107813240779");
+        when(mMockTelephonyManagerForSubId.getSimOperator()).thenReturn("23410");
+        when(mMockTelephonyManagerForSubId.getIccAuthentication(
+                    TelephonyManager.APPTYPE_USIM, TelephonyManager.AUTHTYPE_EAP_AKA,
+                    "EFzmUpAo8crIBONxlqhyb4EQa4pFhq3pAACzVOE8Zggz6A=="))
+                .thenReturn(null);
+        String challenge = "AQIAfBcBAAABBQAAXOZSkCjxysgE4"
+                + "3GWqHJvgQIFAABrikWGrekAALNU4TxmCDPoCwUAAJT0nqXeAYlqzT0UGXINENWBBQAA7z3fhImk"
+                + "q+vcCKWIZBdvuYIJAAAPRUFp7KWFo+Thr78Qj9hEkB2zA0i6KakODsufBC+BJQ==";
+
+        EapAkaHelper.EapAkaResponse response = mEapAkaHelper.getEapAkaResponse(challenge);
+
+        assertThat(response).isNull();
+    }
 }