aboutsummaryrefslogtreecommitdiff
path: root/java/com/android
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:07:32 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:07:32 +0000
commitbc8943635143b878e8acb9b54042b0e15282c0c4 (patch)
treed53c9c8b58b0e475e5774751728d52fa88f8259b /java/com/android
parent0a604fcbe788442a585ec3169a6518cf2acbda83 (diff)
parent1a392f75763d5679f669b26d97269f275491e79e (diff)
downloadservice_entitlement-android14-mainline-tethering-release.tar.gz
Change-Id: I3890d0d581f36aa1064e7297b2119bc680d003c5
Diffstat (limited to 'java/com/android')
-rw-r--r--java/com/android/libraries/entitlement/EsimOdsaOperation.java49
-rw-r--r--java/com/android/libraries/entitlement/ServiceEntitlement.java78
-rw-r--r--java/com/android/libraries/entitlement/ServiceEntitlementException.java5
-rw-r--r--java/com/android/libraries/entitlement/ServiceEntitlementRequest.java29
-rw-r--r--java/com/android/libraries/entitlement/eapaka/EapAkaApi.java199
-rw-r--r--java/com/android/libraries/entitlement/http/HttpClient.java35
-rw-r--r--java/com/android/libraries/entitlement/http/HttpResponse.java7
7 files changed, 351 insertions, 51 deletions
diff --git a/java/com/android/libraries/entitlement/EsimOdsaOperation.java b/java/com/android/libraries/entitlement/EsimOdsaOperation.java
index 20f4fa3..9a3eae6 100644
--- a/java/com/android/libraries/entitlement/EsimOdsaOperation.java
+++ b/java/com/android/libraries/entitlement/EsimOdsaOperation.java
@@ -17,6 +17,7 @@
package com.android.libraries.entitlement;
import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
/**
* HTTP request parameters specific to on device service actiavation (ODSA). See GSMA spec TS.43
@@ -40,6 +41,10 @@ public abstract class EsimOdsaOperation {
* OSDA operation: AcquireConfiguration.
*/
public static final String OPERATION_ACQUIRE_CONFIGURATION = "AcquireConfiguration";
+ /**
+ * OSDA operation: AcquireTemporaryToken.
+ */
+ public static final String OPERATION_ACQUIRE_TEMPORARY_TOKEN = "AcquireTemporaryToken";
/**
* Indicates that operation_type is not set.
@@ -97,6 +102,12 @@ public abstract class EsimOdsaOperation {
public abstract int operationType();
/**
+ * Returns the comma separated list of operation targets used with temporary token from
+ * AcquireTemporaryToken operation. Used by HTTP parameter "operation_targets".
+ */
+ public abstract ImmutableList<String> operationTargets();
+
+ /**
* Returns the unique identifier of the companion device, like IMEI. Used by HTTP parameter
* "companion_terminal_id".
*/
@@ -170,6 +181,18 @@ public abstract class EsimOdsaOperation {
*/
public abstract String targetTerminalEid();
+
+ /**
+ * Returns the unique identifier of the old device eSIM, like the IMEI associated with the
+ * eSIM. Used by HTTP parameter "old_terminal_id".
+ */
+ public abstract String oldTerminalId();
+
+ /**
+ * Returns the ICCID of old device eSIM. Used by HTTP parameter "old_terminal_iccid".
+ */
+ public abstract String oldTerminalIccid();
+
/**
* Returns a new {@link Builder} object.
*/
@@ -177,6 +200,7 @@ public abstract class EsimOdsaOperation {
return new AutoValue_EsimOdsaOperation.Builder()
.setOperation("")
.setOperationType(OPERATION_TYPE_NOT_SET)
+ .setOperationTargets(ImmutableList.of())
.setCompanionTerminalId("")
.setCompanionTerminalVendor("")
.setCompanionTerminalModel("")
@@ -189,7 +213,9 @@ public abstract class EsimOdsaOperation {
.setTerminalEid("")
.setTargetTerminalId("")
.setTargetTerminalIccid("")
- .setTargetTerminalEid("");
+ .setTargetTerminalEid("")
+ .setOldTerminalId("")
+ .setOldTerminalIccid("");
}
/**
@@ -230,6 +256,12 @@ public abstract class EsimOdsaOperation {
public abstract Builder setOperationType(int value);
/**
+ * Sets the operation targets to be used with temporary token from AcquireTemporaryToken
+ * operation. Used by HTTP parameter "operation_targets" if set.
+ */
+ public abstract Builder setOperationTargets(ImmutableList<String> value);
+
+ /**
* Sets the unique identifier of the companion device, like IMEI. Used by HTTP parameter
* "companion_terminal_id" if set.
*
@@ -336,6 +368,21 @@ public abstract class EsimOdsaOperation {
*/
public abstract Builder setTargetTerminalEid(String value);
+ /**
+ * Sets the unique identifier of the old device eSIM, like the IMEI associated with the
+ * eSIM. Used by HTTP parameter "old_terminal_id" if set.
+ *
+ * <p>Used by primary device ODSA operation.
+ */
+ public abstract Builder setOldTerminalId(String value);
+
+ /**
+ * Sets the ICCID old device eSIM. Used by HTTP parameter "old_terminal_iccid" if set.
+ *
+ * <p>Used by primary device ODSA operation.
+ */
+ public abstract Builder setOldTerminalIccid(String value);
+
public abstract EsimOdsaOperation build();
}
}
diff --git a/java/com/android/libraries/entitlement/ServiceEntitlement.java b/java/com/android/libraries/entitlement/ServiceEntitlement.java
index d723e4c..c0d6d55 100644
--- a/java/com/android/libraries/entitlement/ServiceEntitlement.java
+++ b/java/com/android/libraries/entitlement/ServiceEntitlement.java
@@ -25,6 +25,8 @@ import com.android.libraries.entitlement.eapaka.EapAkaApi;
import com.google.common.collect.ImmutableList;
+import java.util.List;
+
/**
* Implemnets protocol for carrier service entitlement configuration query and operation, based on
* GSMA TS.43 spec.
@@ -50,6 +52,10 @@ public class ServiceEntitlement {
* App ID for on device service activation (OSDA) for primary device.
*/
public static final String APP_ODSA_PRIMARY = "ap2009";
+ /**
+ * App ID for data plan information entitlement.
+ */
+ public static final String APP_DATA_PLAN_BOOST = "ap2010";
private final CarrierConfig carrierConfig;
private final EapAkaApi eapAkaApi;
@@ -67,8 +73,63 @@ public class ServiceEntitlement {
* for how to get the subscroption ID.
*/
public ServiceEntitlement(Context context, CarrierConfig carrierConfig, int simSubscriptionId) {
+ this(
+ context,
+ carrierConfig,
+ simSubscriptionId,
+ /* saveHttpHistory= */ false,
+ /* bypassEapAkaResponse= */ "");
+ }
+
+ /**
+ * Creates an instance for service entitlement configuration query and operation for the
+ * carrier.
+ *
+ * @param context context of application
+ * @param carrierConfig carrier specific configs used in the queries and operations.
+ * @param simSubscriptionId the subscroption ID of the carrier's SIM on device. This indicates
+ * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA authentication with. See {@link
+ * android.telephony.SubscriptionManager} for how to get the subscroption ID.
+ * @param saveHttpHistory set to {@code true} to save the history of request and response which
+ * can later be retrieved by {@code getHistory()}. Intended for debugging.
+ */
+ public ServiceEntitlement(
+ Context context,
+ CarrierConfig carrierConfig,
+ int simSubscriptionId,
+ boolean saveHttpHistory) {
+ this(
+ context,
+ carrierConfig,
+ simSubscriptionId,
+ saveHttpHistory,
+ /* bypassEapAkaResponse= */ "");
+ }
+
+ /**
+ * Creates an instance for service entitlement configuration query and operation for the
+ * carrier.
+ *
+ * @param context context of application
+ * @param carrierConfig carrier specific configs used in the queries and operations.
+ * @param simSubscriptionId the subscroption ID of the carrier's SIM on device. This indicates
+ * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA authentication with. See {@link
+ * android.telephony.SubscriptionManager} for how to get the subscroption ID.
+ * @param saveHttpHistory set to {@code true} to save the history of request and response which
+ * can later be retrieved by {@code getHistory()}. Intended for debugging.
+ * @param bypassEapAkaResponse set to non empty string to bypass EAP-AKA authentication.
+ * The client will accept any challenge from the server and return this string as a
+ * response. Must not be {@code null}. Intended for testing.
+ */
+ public ServiceEntitlement(
+ Context context,
+ CarrierConfig carrierConfig,
+ int simSubscriptionId,
+ boolean saveHttpHistory,
+ String bypassEapAkaResponse) {
this.carrierConfig = carrierConfig;
- this.eapAkaApi = new EapAkaApi(context, simSubscriptionId);
+ this.eapAkaApi =
+ new EapAkaApi(context, simSubscriptionId, saveHttpHistory, bypassEapAkaResponse);
}
@VisibleForTesting
@@ -151,4 +212,19 @@ public class ServiceEntitlement {
throws ServiceEntitlementException {
return eapAkaApi.performEsimOdsaOperation(appId, carrierConfig, request, operation);
}
+
+ /**
+ * Retrieves the history of past HTTP request and responses if {@code saveHttpHistory} was set
+ * in constructor.
+ */
+ public List<String> getHistory() {
+ return eapAkaApi.getHistory();
+ }
+
+ /**
+ * Clears the history of past HTTP request and responses.
+ */
+ public void clearHistory() {
+ eapAkaApi.clearHistory();
+ }
}
diff --git a/java/com/android/libraries/entitlement/ServiceEntitlementException.java b/java/com/android/libraries/entitlement/ServiceEntitlementException.java
index 45b1b9b..b1cb50f 100644
--- a/java/com/android/libraries/entitlement/ServiceEntitlementException.java
+++ b/java/com/android/libraries/entitlement/ServiceEntitlementException.java
@@ -43,6 +43,11 @@ public class ServiceEntitlementException extends Exception {
* synchronization" procedure as defined in RFC 4187.
*/
public static final int ERROR_EAP_AKA_SYNCHRONIZATION_FAILURE = 21;
+ /**
+ * EAP-AKA failure that happens when the client fails to authenticate within the maximum number
+ * of attempts
+ */
+ public static final int ERROR_EAP_AKA_FAILURE = 21;
// HTTP related failures
/**
diff --git a/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java b/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java
index c7b0ad3..e0ecbf7 100644
--- a/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java
+++ b/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java
@@ -62,6 +62,11 @@ public abstract class ServiceEntitlementRequest {
public abstract String authenticationToken();
/**
+ * Returns the temporary token. Used by HTTP parameter "temporary_token".
+ */
+ public abstract String temporaryToken();
+
+ /**
* Returns the unique identifier of the device like IMEI. Used by HTTP parameter "terminal_id".
*/
public abstract String terminalId();
@@ -118,6 +123,11 @@ public abstract class ServiceEntitlementRequest {
public abstract String acceptContentType();
/**
+ * Returns the boost type for premium network. Used for premium network slice entitlement.
+ */
+ public abstract String boostType();
+
+ /**
* Returns a new {@link Builder} object.
*/
public static Builder builder() {
@@ -125,6 +135,7 @@ public abstract class ServiceEntitlementRequest {
.setConfigurationVersion(DEFAULT_CONFIGURATION_VERSION)
.setEntitlementVersion(DEFAULT_ENTITLEMENT_VERSION)
.setAuthenticationToken("")
+ .setTemporaryToken("")
.setTerminalId("")
.setTerminalVendor(Build.MANUFACTURER)
.setTerminalModel(Build.MODEL)
@@ -133,7 +144,8 @@ public abstract class ServiceEntitlementRequest {
.setAppVersion("")
.setNotificationToken("")
.setNotificationAction(NOTICATION_ACTION_ENABLE_FCM)
- .setAcceptContentType(ACCEPT_CONTENT_TYPE_JSON_AND_XML);
+ .setAcceptContentType(ACCEPT_CONTENT_TYPE_JSON_AND_XML)
+ .setBoostType("");
}
/**
@@ -167,6 +179,13 @@ public abstract class ServiceEntitlementRequest {
public abstract Builder setAuthenticationToken(String value);
/**
+ * Sets the temporary token. Used by HTTP parameter "temporary_token".
+ *
+ * <p>Optional.
+ */
+ public abstract Builder setTemporaryToken(String value);
+
+ /**
* Sets the unique identifier of the device like IMEI. Used by HTTP parameter
* "terminal_id".
*
@@ -243,6 +262,14 @@ public abstract class ServiceEntitlementRequest {
*/
public abstract Builder setAcceptContentType(String contentType);
+ /**
+ * Sets the boost type for premium network. Used by HTTP parameter
+ * "boost_type" in case of premium network slice entitlement.
+ *
+ * <p>Optional.
+ */
+ public abstract Builder setBoostType(String value);
+
public abstract ServiceEntitlementRequest build();
}
}
diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
index 4482bf7..be41ca7 100644
--- a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
+++ b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
@@ -16,6 +16,7 @@
package com.android.libraries.entitlement.eapaka;
+import static com.android.libraries.entitlement.ServiceEntitlementException.ERROR_EAP_AKA_FAILURE;
import static com.android.libraries.entitlement.ServiceEntitlementException.ERROR_EAP_AKA_SYNCHRONIZATION_FAILURE;
import static com.android.libraries.entitlement.ServiceEntitlementException.ERROR_MALFORMED_HTTP_RESPONSE;
@@ -33,6 +34,7 @@ import com.android.libraries.entitlement.EsimOdsaOperation;
import com.android.libraries.entitlement.ServiceEntitlementException;
import com.android.libraries.entitlement.ServiceEntitlementRequest;
import com.android.libraries.entitlement.http.HttpClient;
+import com.android.libraries.entitlement.http.HttpConstants.ContentType;
import com.android.libraries.entitlement.http.HttpConstants.RequestMethod;
import com.android.libraries.entitlement.http.HttpRequest;
import com.android.libraries.entitlement.http.HttpResponse;
@@ -43,6 +45,8 @@ import com.google.common.net.HttpHeaders;
import org.json.JSONException;
import org.json.JSONObject;
+import java.util.List;
+
public class EapAkaApi {
private static final String TAG = "ServiceEntitlement";
@@ -58,6 +62,7 @@ public class EapAkaApi {
private static final String EAP_ID = "EAP_ID";
private static final String IMSI = "IMSI";
private static final String TOKEN = "token";
+ private static final String TEMPORARY_TOKEN = "temporary_token";
private static final String NOTIF_ACTION = "notif_action";
private static final String NOTIF_TOKEN = "notif_token";
private static final String APP_VERSION = "app_version";
@@ -65,6 +70,7 @@ public class EapAkaApi {
private static final String OPERATION = "operation";
private static final String OPERATION_TYPE = "operation_type";
+ private static final String OPERATION_TARGETS = "operation_targets";
private static final String COMPANION_TERMINAL_ID = "companion_terminal_id";
private static final String COMPANION_TERMINAL_VENDOR = "companion_terminal_vendor";
private static final String COMPANION_TERMINAL_MODEL = "companion_terminal_model";
@@ -82,22 +88,38 @@ public class EapAkaApi {
private static final String TARGET_TERMINAL_ICCID = "target_terminal_iccid";
private static final String TARGET_TERMINAL_EID = "target_terminal_eid";
- // In case of EAP-AKA synchronization failure, we try to recover for at most two times.
- private static final int FOLLOW_SYNC_FAILURE_MAX_COUNT = 2;
+ private static final String OLD_TERMINAL_ID = "old_terminal_id";
+ private static final String OLD_TERMINAL_ICCID = "old_terminal_iccid";
+
+ private static final String BOOST_TYPE = "boost_type";
+
+ // In case of EAP-AKA synchronization failure or another challenge, we try to authenticate for
+ // at most three times.
+ private static final int MAX_EAP_AKA_ATTEMPTS = 3;
private final Context mContext;
private final int mSimSubscriptionId;
private final HttpClient mHttpClient;
-
- public EapAkaApi(Context context, int simSubscriptionId) {
- this(context, simSubscriptionId, new HttpClient());
+ private final String mBypassEapAkaResponse;
+
+ public EapAkaApi(
+ Context context,
+ int simSubscriptionId,
+ boolean saveHistory,
+ String bypassEapAkaResponse) {
+ this(context, simSubscriptionId, new HttpClient(saveHistory), bypassEapAkaResponse);
}
@VisibleForTesting
- EapAkaApi(Context context, int simSubscriptionId, HttpClient httpClient) {
+ EapAkaApi(
+ Context context,
+ int simSubscriptionId,
+ HttpClient httpClient,
+ String bypassEapAkaResponse) {
this.mContext = context;
this.mSimSubscriptionId = simSubscriptionId;
this.mHttpClient = httpClient;
+ this.mBypassEapAkaResponse = bypassEapAkaResponse;
}
/**
@@ -126,10 +148,17 @@ public class EapAkaApi {
urlBuilder.toString(),
carrierConfig,
ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
+ String eapAkaChallenge = getEapAkaChallenge(challengeResponse);
+ if (eapAkaChallenge == null) {
+ throw new ServiceEntitlementException(
+ ERROR_MALFORMED_HTTP_RESPONSE,
+ "Failed to parse EAP-AKA challenge: " + challengeResponse.body());
+ }
return respondToEapAkaChallenge(
carrierConfig,
- challengeResponse,
- FOLLOW_SYNC_FAILURE_MAX_COUNT,
+ eapAkaChallenge,
+ challengeResponse.cookies(),
+ MAX_EAP_AKA_ATTEMPTS,
request.acceptContentType())
.body();
}
@@ -139,55 +168,79 @@ public class EapAkaApi {
* Sends a follow-up HTTP request to the HTTP {@code response} using the same cookie, and
* returns the follow-up HTTP response.
*
- * <p>The {@code response} should contain a EAP-AKA challenge from server, and the
- * follow-up request could contain:
+ * <p>The {@code eapAkaChallenge} should be the EAP-AKA challenge from server, and the follow-up
+ * request could contain:
*
* <ul>
- * <li>The EAP-AKA response message, and the follow-up response should contain the
- * service entitlement configuration; or,
- * <li>The EAP-AKA synchronization failure message, and the follow-up response should
- * contain the new EAP-AKA challenge. Then this method calls itself to follow-up
- * the new challenge and return a new response, if {@code followSyncFailureCount}
- * is greater than zero. When this method call itself {@code followSyncFailureCount} is
- * reduced by one to prevent infinite loop (unlikely in practice, but just in case).
+ * <li>The EAP-AKA response message, and the follow-up response should contain the service
+ * entitlement configuration, or another EAP-AKA challenge in which case the method calls
+ * if {@code remainingAttempts} is greater than zero (If {@code remainingAttempts} reaches
+ * 0, the method will throw ServiceEntitlementException) ; or
+ * <li>The EAP-AKA synchronization failure message, and the follow-up response should contain
+ * the new EAP-AKA challenge. Then this method calls itself to follow-up the new challenge
+ * and return a new response, as long as {@code remainingAttempts} is greater than zero.
* </ul>
*
* @param response Challenge response from server which its content type is JSON
*/
private HttpResponse respondToEapAkaChallenge(
CarrierConfig carrierConfig,
- HttpResponse response,
- int followSyncFailureCount,
+ String eapAkaChallenge,
+ ImmutableList<String> cookies,
+ int remainingAttempts,
String contentType)
throws ServiceEntitlementException {
- String eapAkaChallenge;
- try {
- eapAkaChallenge = new JSONObject(response.body()).getString(EAP_CHALLENGE_RESPONSE);
- } catch (JSONException jsonException) {
- throw new ServiceEntitlementException(
- ERROR_MALFORMED_HTTP_RESPONSE, "Failed to parse json object", jsonException);
+ if (!mBypassEapAkaResponse.isEmpty()) {
+ return challengeResponse(mBypassEapAkaResponse, carrierConfig, cookies, contentType);
}
+
EapAkaChallenge challenge = EapAkaChallenge.parseEapAkaChallenge(eapAkaChallenge);
EapAkaResponse eapAkaResponse =
EapAkaResponse.respondToEapAkaChallenge(mContext, mSimSubscriptionId, challenge);
- // This could be a successful authentication, or synchronization failure.
- if (eapAkaResponse.response() != null) { // successful authentication
- return challengeResponse(
- eapAkaResponse.response(),
- carrierConfig,
- response.cookies(),
- contentType);
+ // This could be a successful authentication, another challenge, or synchronization failure.
+ if (eapAkaResponse.response() != null) {
+ HttpResponse response =
+ challengeResponse(
+ eapAkaResponse.response(), carrierConfig, cookies, contentType);
+ String nextEapAkaChallenge = getEapAkaChallenge(response);
+ // successful authentication
+ if (nextEapAkaChallenge == null) {
+ return response;
+ }
+ // another challenge
+ Log.d(TAG, "Received another challenge");
+ if (remainingAttempts > 0) {
+ return respondToEapAkaChallenge(
+ carrierConfig,
+ nextEapAkaChallenge,
+ cookies,
+ remainingAttempts - 1,
+ contentType);
+ } else {
+ throw new ServiceEntitlementException(
+ ERROR_EAP_AKA_FAILURE, "Unable to EAP-AKA authenticate");
+ }
} else if (eapAkaResponse.synchronizationFailureResponse() != null) {
Log.d(TAG, "synchronization failure");
HttpResponse newChallenge =
challengeResponse(
eapAkaResponse.synchronizationFailureResponse(),
carrierConfig,
- response.cookies(),
+ cookies,
ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
- if (followSyncFailureCount > 0) {
+ String nextEapAkaChallenge = getEapAkaChallenge(newChallenge);
+ if (nextEapAkaChallenge == null) {
+ throw new ServiceEntitlementException(
+ ERROR_MALFORMED_HTTP_RESPONSE,
+ "Failed to parse EAP-AKA challenge: " + newChallenge.body());
+ }
+ if (remainingAttempts > 0) {
return respondToEapAkaChallenge(
- carrierConfig, newChallenge, followSyncFailureCount - 1, contentType);
+ carrierConfig,
+ nextEapAkaChallenge,
+ cookies,
+ remainingAttempts - 1,
+ contentType);
} else {
throw new ServiceEntitlementException(
ERROR_EAP_AKA_SYNCHRONIZATION_FAILURE,
@@ -241,7 +294,8 @@ public class EapAkaApi {
appendParametersForServiceEntitlementRequest(urlBuilder, ImmutableList.of(appId), request);
appendParametersForEsimOdsaOperation(urlBuilder, odsaOperation);
- if (!TextUtils.isEmpty(request.authenticationToken())) {
+ if (!TextUtils.isEmpty(request.authenticationToken())
+ || !TextUtils.isEmpty(request.temporaryToken())) {
// Fast Re-Authentication flow with pre-existing auth token
Log.d(TAG, "Fast Re-Authentication");
return httpGet(
@@ -254,10 +308,17 @@ public class EapAkaApi {
urlBuilder.toString(),
carrierConfig,
ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
+ String eapAkaChallenge = getEapAkaChallenge(challengeResponse);
+ if (eapAkaChallenge == null) {
+ throw new ServiceEntitlementException(
+ ERROR_MALFORMED_HTTP_RESPONSE,
+ "Failed to parse EAP-AKA challenge: " + challengeResponse.body());
+ }
return respondToEapAkaChallenge(
carrierConfig,
- challengeResponse,
- FOLLOW_SYNC_FAILURE_MAX_COUNT,
+ eapAkaChallenge,
+ challengeResponse.cookies(),
+ MAX_EAP_AKA_ATTEMPTS,
request.acceptContentType())
.body();
}
@@ -268,17 +329,20 @@ public class EapAkaApi {
ServiceEntitlementRequest request) {
TelephonyManager telephonyManager = mContext.getSystemService(
TelephonyManager.class).createForSubscriptionId(mSimSubscriptionId);
- if (TextUtils.isEmpty(request.authenticationToken())) {
+ if (!TextUtils.isEmpty(request.authenticationToken())) {
+ // IMSI and token required for fast AuthN.
+ urlBuilder
+ .appendQueryParameter(IMSI, telephonyManager.getSubscriberId())
+ .appendQueryParameter(TOKEN, request.authenticationToken());
+ } else if (!TextUtils.isEmpty(request.temporaryToken())) {
+ // temporary_token required for fast AuthN.
+ urlBuilder.appendQueryParameter(TEMPORARY_TOKEN, request.temporaryToken());
+ } else {
// EAP_ID required for initial AuthN
urlBuilder.appendQueryParameter(
EAP_ID,
getImsiEap(telephonyManager.getSimOperator(),
telephonyManager.getSubscriberId()));
- } else {
- // IMSI and token required for fast AuthN.
- urlBuilder
- .appendQueryParameter(IMSI, telephonyManager.getSubscriberId())
- .appendQueryParameter(TOKEN, request.authenticationToken());
}
if (!TextUtils.isEmpty(request.notificationToken())) {
@@ -298,6 +362,7 @@ public class EapAkaApi {
// Optional query parameters, append them if not empty
appendOptionalQueryParameter(urlBuilder, APP_VERSION, request.appVersion());
appendOptionalQueryParameter(urlBuilder, APP_NAME, request.appName());
+ appendOptionalQueryParameter(urlBuilder, BOOST_TYPE, request.boostType());
for (String appId : appIds) {
urlBuilder.appendQueryParameter(APP, appId);
@@ -320,6 +385,10 @@ public class EapAkaApi {
urlBuilder.appendQueryParameter(OPERATION_TYPE,
Integer.toString(odsaOperation.operationType()));
}
+ appendOptionalQueryParameter(
+ urlBuilder,
+ OPERATION_TARGETS,
+ TextUtils.join(",", odsaOperation.operationTargets()));
appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_ID,
odsaOperation.companionTerminalId());
appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_VENDOR,
@@ -345,6 +414,10 @@ public class EapAkaApi {
odsaOperation.targetTerminalIccid());
appendOptionalQueryParameter(urlBuilder, TARGET_TERMINAL_EID,
odsaOperation.targetTerminalEid());
+ appendOptionalQueryParameter(urlBuilder, OLD_TERMINAL_ICCID,
+ odsaOperation.oldTerminalIccid());
+ appendOptionalQueryParameter(urlBuilder, OLD_TERMINAL_ID,
+ odsaOperation.oldTerminalId());
}
private HttpResponse httpGet(String url, CarrierConfig carrierConfig, String contentType)
@@ -366,6 +439,30 @@ public class EapAkaApi {
}
}
+ @Nullable
+ private String getEapAkaChallenge(HttpResponse response) throws ServiceEntitlementException {
+ String eapAkaChallenge = null;
+ String responseBody = response.body();
+ if (response.contentType() == ContentType.JSON) {
+ try {
+ eapAkaChallenge =
+ new JSONObject(responseBody).optString(EAP_CHALLENGE_RESPONSE, null);
+ } catch (JSONException jsonException) {
+ throw new ServiceEntitlementException(
+ ERROR_MALFORMED_HTTP_RESPONSE,
+ "Failed to parse json object",
+ jsonException);
+ }
+ } else if (response.contentType() == ContentType.XML) {
+ // TODO: possibly support parsing eap-relay-packet in XML format
+ return null;
+ } else {
+ throw new ServiceEntitlementException(
+ ERROR_MALFORMED_HTTP_RESPONSE, "Unknown HTTP content type");
+ }
+ return eapAkaChallenge;
+ }
+
/**
* Returns the IMSI EAP value. The resulting realm part of the Root NAI in 3GPP TS 23.003 clause
* 19.3.2 will be in the form:
@@ -385,4 +482,18 @@ public class EapAkaApi {
}
return "0" + imsi + "@nai.epc.mnc" + mnc + ".mcc" + mcc + ".3gppnetwork.org";
}
+
+ /**
+ * Retrieves the history of past HTTP request and responses.
+ */
+ public List<String> getHistory() {
+ return mHttpClient.getHistory();
+ }
+
+ /**
+ * Clears the history of past HTTP request and responses.
+ */
+ public void clearHistory() {
+ mHttpClient.clearHistory();
+ }
}
diff --git a/java/com/android/libraries/entitlement/http/HttpClient.java b/java/com/android/libraries/entitlement/http/HttpClient.java
index 9ccb5ee..f2b394d 100644
--- a/java/com/android/libraries/entitlement/http/HttpClient.java
+++ b/java/com/android/libraries/entitlement/http/HttpClient.java
@@ -47,6 +47,7 @@ import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -55,9 +56,19 @@ public class HttpClient {
private static final String TAG = "ServiceEntitlement";
private HttpURLConnection mConnection;
+ private boolean mSaveHistory;
+ private ArrayList<String> mHistory;
+
+ public HttpClient(boolean saveHistory) {
+ mSaveHistory = saveHistory;
+ mHistory = new ArrayList<>();
+ }
@WorkerThread
public HttpResponse request(HttpRequest request) throws ServiceEntitlementException {
+ if (mSaveHistory) {
+ mHistory.add(request.toString());
+ }
logPii("HttpClient.request url: " + request.url());
createConnection(request);
logPii("HttpClient.request headers (partial): " + mConnection.getRequestProperties());
@@ -73,18 +84,38 @@ public class HttpClient {
}
mConnection.connect(); // This is to trigger SocketTimeoutException early
HttpResponse response = getHttpResponse(mConnection);
- Log.d(TAG, "HttpClient.response : " + response);
+ Log.d(TAG, "HttpClient.response : " + response.toShortDebugString());
+ if (mSaveHistory) {
+ mHistory.add(response.toString());
+ }
return response;
} catch (IOException ioe) {
throw new ServiceEntitlementException(
ERROR_HTTP_STATUS_NOT_SUCCESS,
- StreamUtils.inputStreamToStringSafe(mConnection.getErrorStream()),
+ "Connection error stream: "
+ + StreamUtils.inputStreamToStringSafe(mConnection.getErrorStream())
+ + " IOException: "
+ + ioe.toString(),
ioe);
} finally {
closeConnection();
}
}
+ /**
+ * Retrieves the history of past HTTP request and responses.
+ */
+ public List<String> getHistory() {
+ return mHistory;
+ }
+
+ /**
+ * Clears the history of past HTTP request and responses.
+ */
+ public void clearHistory() {
+ mHistory.clear();
+ }
+
private void createConnection(HttpRequest request) throws ServiceEntitlementException {
try {
URL url = new URL(request.url());
diff --git a/java/com/android/libraries/entitlement/http/HttpResponse.java b/java/com/android/libraries/entitlement/http/HttpResponse.java
index f495578..142639e 100644
--- a/java/com/android/libraries/entitlement/http/HttpResponse.java
+++ b/java/com/android/libraries/entitlement/http/HttpResponse.java
@@ -74,8 +74,11 @@ public abstract class HttpResponse {
.setCookies(ImmutableList.of());
}
- @Override
- public final String toString() {
+ /**
+ * Returns a short string representation for debugging purposes. Doesn't include the cookie or
+ * full body to prevent leaking sensitive data.
+ */
+ public String toShortDebugString() {
return new StringBuilder("HttpResponse{")
.append("contentType=")
.append(contentType())