1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.libraries.entitlement; 18 19 import android.content.Context; 20 21 import androidx.annotation.NonNull; 22 import androidx.annotation.VisibleForTesting; 23 24 import com.android.libraries.entitlement.eapaka.EapAkaApi; 25 import com.android.libraries.entitlement.http.HttpResponse; 26 import com.android.libraries.entitlement.utils.DebugUtils; 27 import com.android.libraries.entitlement.utils.Ts43Constants; 28 29 import com.google.common.collect.ImmutableList; 30 31 import java.util.List; 32 33 /** 34 * Implements protocol for carrier service entitlement configuration query and operation, based on 35 * GSMA TS.43 spec. 36 */ 37 public class ServiceEntitlement { 38 /** 39 * App ID for Voice-Over-LTE entitlement. 40 */ 41 public static final String APP_VOLTE = Ts43Constants.APP_VOLTE; 42 /** 43 * App ID for Voice-Over-WiFi entitlement. 44 */ 45 public static final String APP_VOWIFI = Ts43Constants.APP_VOWIFI; 46 /** 47 * App ID for SMS-Over-IP entitlement. 48 */ 49 public static final String APP_SMSOIP = Ts43Constants.APP_SMSOIP; 50 /** 51 * App ID for on device service activation (ODSA) for companion device. 52 */ 53 public static final String APP_ODSA_COMPANION = Ts43Constants.APP_ODSA_COMPANION; 54 /** 55 * App ID for on device service activation (ODSA) for primary device. 56 */ 57 public static final String APP_ODSA_PRIMARY = Ts43Constants.APP_ODSA_PRIMARY; 58 /** 59 * App ID for data plan information entitlement. 60 */ 61 public static final String APP_DATA_PLAN_BOOST = Ts43Constants.APP_DATA_PLAN_BOOST; 62 63 /** 64 * App ID for server initiated requests, entitlement and activation. 65 */ 66 public static final String APP_ODSA_SERVER_INITIATED_REQUESTS = 67 Ts43Constants.APP_ODSA_SERVER_INITIATED_REQUESTS; 68 69 /** 70 * App ID for direct carrier billing. 71 */ 72 public static final String APP_DIRECT_CARRIER_BILLING = 73 Ts43Constants.APP_DIRECT_CARRIER_BILLING; 74 75 /** 76 * App ID for private user identity. 77 */ 78 public static final String APP_PRIVATE_USER_IDENTITY = Ts43Constants.APP_PRIVATE_USER_IDENTITY; 79 80 /** 81 * App ID for phone number information. 82 */ 83 public static final String APP_PHONE_NUMBER_INFORMATION = 84 Ts43Constants.APP_PHONE_NUMBER_INFORMATION; 85 86 /** 87 * App ID for satellite entitlement. 88 */ 89 public static final String APP_SATELLITE_ENTITLEMENT = Ts43Constants.APP_SATELLITE_ENTITLEMENT; 90 91 private final CarrierConfig carrierConfig; 92 private final EapAkaApi eapAkaApi; 93 private ServiceEntitlementRequest mOidcRequest; 94 /** 95 * Creates an instance for service entitlement configuration query and operation for the 96 * carrier. 97 * 98 * @param context context of application 99 * @param carrierConfig carrier specific configs used in the queries and operations. 100 * @param simSubscriptionId the subscription ID of the carrier's SIM on device. This indicates 101 * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA 102 * authentication with. See 103 * {@link android.telephony.SubscriptionManager} 104 * for how to get the subscription ID. 105 */ ServiceEntitlement(Context context, CarrierConfig carrierConfig, int simSubscriptionId)106 public ServiceEntitlement(Context context, CarrierConfig carrierConfig, int simSubscriptionId) { 107 this(context, carrierConfig, simSubscriptionId, /* saveHttpHistory= */ false); 108 } 109 110 /** 111 * Creates an instance for service entitlement configuration query and operation for the 112 * carrier. 113 * 114 * @param context context of application 115 * @param carrierConfig carrier specific configs used in the queries and operations. 116 * @param simSubscriptionId the subscription ID of the carrier's SIM on device. This indicates 117 * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA authentication with. See {@link 118 * android.telephony.SubscriptionManager} for how to get the subscription ID. 119 * @param saveHttpHistory set to {@code true} to save the history of request and response which 120 * can later be retrieved by {@code getHistory()}. Intended for debugging. 121 */ ServiceEntitlement( Context context, CarrierConfig carrierConfig, int simSubscriptionId, boolean saveHttpHistory)122 public ServiceEntitlement( 123 Context context, 124 CarrierConfig carrierConfig, 125 int simSubscriptionId, 126 boolean saveHttpHistory) { 127 this( 128 context, 129 carrierConfig, 130 simSubscriptionId, 131 saveHttpHistory, 132 DebugUtils.getBypassEapAkaResponse()); 133 } 134 135 /** 136 * Creates an instance for service entitlement configuration query and operation for the 137 * carrier. 138 * 139 * @param context context of application 140 * @param carrierConfig carrier specific configs used in the queries and operations. 141 * @param simSubscriptionId the subscription ID of the carrier's SIM on device. This indicates 142 * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA authentication with. See {@link 143 * android.telephony.SubscriptionManager} for how to get the subscription ID. 144 * @param saveHttpHistory set to {@code true} to save the history of request and response which 145 * can later be retrieved by {@code getHistory()}. Intended for debugging. 146 * @param bypassEapAkaResponse set to non empty string to bypass EAP-AKA authentication. 147 * The client will accept any challenge from the server and return this string as a 148 * response. Must not be {@code null}. Intended for testing. 149 */ ServiceEntitlement( Context context, CarrierConfig carrierConfig, int simSubscriptionId, boolean saveHttpHistory, String bypassEapAkaResponse)150 public ServiceEntitlement( 151 Context context, 152 CarrierConfig carrierConfig, 153 int simSubscriptionId, 154 boolean saveHttpHistory, 155 String bypassEapAkaResponse) { 156 this.carrierConfig = carrierConfig; 157 this.eapAkaApi = 158 new EapAkaApi(context, simSubscriptionId, saveHttpHistory, bypassEapAkaResponse); 159 } 160 161 @VisibleForTesting ServiceEntitlement(CarrierConfig carrierConfig, EapAkaApi eapAkaApi)162 ServiceEntitlement(CarrierConfig carrierConfig, EapAkaApi eapAkaApi) { 163 this.carrierConfig = carrierConfig; 164 this.eapAkaApi = eapAkaApi; 165 } 166 167 /** 168 * Retrieves service entitlement configuration. For on device service activation (ODSA) of eSIM 169 * for companion/primary devices, use {@link #performEsimOdsa} instead. 170 * 171 * <p>Supported {@code appId}: {@link #APP_VOLTE}, {@link #APP_VOWIFI}, {@link #APP_SMSOIP}. 172 * 173 * <p>This method sends an HTTP GET request to entitlement server, responds to EAP-AKA 174 * challenge if needed, and returns the raw configuration doc as a string. The following 175 * parameters are set in the HTTP request: 176 * 177 * <ul> 178 * <li>"app": {@code appId} 179 * <li>"vers": 0, or {@code request.configurationVersion()} if it's not 0. 180 * <li>"entitlement_version": "2.0", or {@code request.entitlementVersion()} if it's not empty. 181 * <li>"token": not set, or {@code request.authenticationToken()} if it's not empty. 182 * <li>"IMSI": if "token" is set, set to {@link android.telephony.TelephonyManager#getImei}. 183 * <li>"EAP_ID": if "token" is not set, set this parameter to trigger embedded EAP-AKA 184 * authentication as described in TS.43 section 2.6.1. Its value is derived from IMSI as per 185 * GSMA spec RCC.14 section C.2. 186 * <li>"terminal_id": IMEI, or {@code request.terminalId()} if it's not empty. 187 * <li>"terminal_vendor": {@link android.os.Build#MANUFACTURER}, or {@code 188 * request.terminalVendor()} if it's not empty. 189 * <li>"terminal_model": {@link android.os.Build#MODEL}, or {@code request.terminalModel()} if 190 * it's not empty. 191 * <li>"terminal_sw_version": {@link android.os.Build.VERSION#BASE_OS}, or {@code 192 * request.terminalSoftwareVersion()} if it's not empty. 193 * <li>"app_name": not set, or {@code request.appName()} if it's not empty. 194 * <li>"app_version": not set, or {@code request.appVersion()} if it's not empty. 195 * <li>"notif_token": not set, or {@code request.notificationToken()} if it's not empty. 196 * <li>"notif_action": {@code request.notificationAction()} if "notif_token" is set, otherwise 197 * not set. 198 * </ul> 199 * 200 * <p>Requires permission: READ_PRIVILEGED_PHONE_STATE, or carrier privilege. 201 * 202 * @param appId an app ID string defined in TS.43 section 2.2, e.g. {@link #APP_VOWIFI}. 203 * @param request contains parameters that can be used in the HTTP request. 204 */ 205 @NonNull queryEntitlementStatus(String appId, ServiceEntitlementRequest request)206 public String queryEntitlementStatus(String appId, ServiceEntitlementRequest request) 207 throws ServiceEntitlementException { 208 return queryEntitlementStatus(ImmutableList.of(appId), request); 209 } 210 211 /** 212 * Retrieves service entitlement configurations for multiple app IDs in one HTTP 213 * request/response. For on device service activation (ODSA) of eSIM for companion/primary 214 * devices, use {@link #performEsimOdsa} instead. 215 * 216 * <p>Same as {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)} except that 217 * multiple "app" parameters will be set in the HTTP request, in the order as they appear in 218 * parameter {@code appIds}. 219 */ 220 @NonNull queryEntitlementStatus(ImmutableList<String> appIds, ServiceEntitlementRequest request)221 public String queryEntitlementStatus(ImmutableList<String> appIds, 222 ServiceEntitlementRequest request) 223 throws ServiceEntitlementException { 224 return getEntitlementStatusResponse(appIds, request).body(); 225 } 226 227 /** 228 * Retrieves service entitlement configurations for multiple app IDs in one HTTP 229 * request/response. For on device service activation (ODSA) of eSIM for companion/primary 230 * devices, use {@link #performEsimOdsa} instead. 231 * 232 * <p>Same as {@link #queryEntitlementStatus(ImmutableList, ServiceEntitlementRequest)} 233 * except that it returns the full HTTP response instead of just the body. 234 */ 235 @NonNull getEntitlementStatusResponse(ImmutableList<String> appIds, ServiceEntitlementRequest request)236 public HttpResponse getEntitlementStatusResponse(ImmutableList<String> appIds, 237 ServiceEntitlementRequest request) 238 throws ServiceEntitlementException { 239 return eapAkaApi.queryEntitlementStatus(appIds, carrierConfig, request); 240 } 241 242 /** 243 * Performs on device service activation (ODSA) of eSIM for companion/primary devices. 244 * 245 * <p>Supported {@code appId}: {@link #APP_ODSA_COMPANION}, {@link #APP_ODSA_PRIMARY}. 246 * 247 * <p>Similar to {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)}, this 248 * method sends an HTTP GET request to entitlement server, responds to EAP-AKA challenge if 249 * needed, and returns the raw configuration doc as a string. Additional parameters from {@code 250 * operation} are set to the HTTP request. See {@link EsimOdsaOperation} for details. 251 */ 252 @NonNull performEsimOdsa( String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation)253 public String performEsimOdsa( 254 String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation) 255 throws ServiceEntitlementException { 256 return getEsimOdsaResponse(appId, request, operation).body(); 257 } 258 259 /** 260 * Retrieves the HTTP response after performing on device service activation (ODSA) of eSIM for 261 * companion/primary devices. 262 * 263 * <p>Same as {@link #performEsimOdsa(String, ServiceEntitlementRequest, EsimOdsaOperation)} 264 * except that it returns the full HTTP response instead of just the body. 265 */ 266 @NonNull getEsimOdsaResponse( String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation)267 public HttpResponse getEsimOdsaResponse( 268 String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation) 269 throws ServiceEntitlementException { 270 return eapAkaApi.performEsimOdsaOperation(appId, carrierConfig, request, operation); 271 } 272 273 /** 274 * Retrieves the endpoint for OpenID Connect(OIDC) authentication. 275 * 276 * <p>Implementation based on section 2.8.2 of TS.43 277 * 278 * <p>The user should call {@link #queryEntitlementStatusFromOidc(String url)} with the 279 * authentication result to retrieve the service entitlement configuration. 280 * 281 * @param appId an app ID string defined in TS.43 section 2.2 282 * @param request contains parameters that can be used in the HTTP request 283 */ 284 @NonNull acquireOidcAuthenticationEndpoint(String appId, ServiceEntitlementRequest request)285 public String acquireOidcAuthenticationEndpoint(String appId, ServiceEntitlementRequest request) 286 throws ServiceEntitlementException { 287 mOidcRequest = request; 288 return eapAkaApi.acquireOidcAuthenticationEndpoint(appId, carrierConfig, request); 289 } 290 291 /** 292 * Retrieves the service entitlement configuration from OIDC authentication result. 293 * 294 * <p>Implementation based on section 2.8.2 of TS.43. 295 * 296 * <p>{@link #acquireOidcAuthenticationEndpoint} must be called before calling this method. 297 * 298 * @param url the redirect url from OIDC authentication result. 299 */ 300 @NonNull queryEntitlementStatusFromOidc(String url)301 public String queryEntitlementStatusFromOidc(String url) throws ServiceEntitlementException { 302 return getEntitlementStatusResponseFromOidc(url).body(); 303 } 304 305 /** 306 * Retrieves the HTTP response containing the service entitlement configuration from 307 * OIDC authentication result. 308 * 309 * <p>Same as {@link #queryEntitlementStatusFromOidc(String)} except that it returns the 310 * full HTTP response instead of just the body. 311 * 312 * @param url the redirect url from OIDC authentication result. 313 */ 314 @NonNull getEntitlementStatusResponseFromOidc(String url)315 public HttpResponse getEntitlementStatusResponseFromOidc(String url) 316 throws ServiceEntitlementException { 317 return eapAkaApi.queryEntitlementStatusFromOidc(url, carrierConfig, mOidcRequest); 318 } 319 320 /** 321 * Retrieves the history of past HTTP request and responses if {@code saveHttpHistory} was set 322 * in constructor. 323 */ 324 @NonNull getHistory()325 public List<String> getHistory() { 326 return eapAkaApi.getHistory(); 327 } 328 329 /** 330 * Clears the history of past HTTP request and responses. 331 */ clearHistory()332 public void clearHistory() { 333 eapAkaApi.clearHistory(); 334 } 335 } 336