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.Nullable; 23 import androidx.annotation.VisibleForTesting; 24 25 import com.android.libraries.entitlement.eapaka.EapAkaApi; 26 import com.android.libraries.entitlement.http.HttpResponse; 27 import com.android.libraries.entitlement.utils.DebugUtils; 28 import com.android.libraries.entitlement.utils.Ts43Constants; 29 30 import com.google.common.collect.ImmutableList; 31 import com.google.common.collect.ImmutableMap; 32 33 import java.util.List; 34 35 /** 36 * Implements protocol for carrier service entitlement configuration query and operation, based on 37 * GSMA TS.43 spec. 38 */ 39 public class ServiceEntitlement { 40 /** 41 * App ID for Voice-Over-LTE entitlement. 42 */ 43 public static final String APP_VOLTE = Ts43Constants.APP_VOLTE; 44 /** 45 * App ID for Voice-Over-WiFi entitlement. 46 */ 47 public static final String APP_VOWIFI = Ts43Constants.APP_VOWIFI; 48 /** 49 * App ID for SMS-Over-IP entitlement. 50 */ 51 public static final String APP_SMSOIP = Ts43Constants.APP_SMSOIP; 52 /** 53 * App ID for on device service activation (ODSA) for companion device. 54 */ 55 public static final String APP_ODSA_COMPANION = Ts43Constants.APP_ODSA_COMPANION; 56 /** 57 * App ID for on device service activation (ODSA) for primary device. 58 */ 59 public static final String APP_ODSA_PRIMARY = Ts43Constants.APP_ODSA_PRIMARY; 60 /** 61 * App ID for data plan information entitlement. 62 */ 63 public static final String APP_DATA_PLAN_BOOST = Ts43Constants.APP_DATA_PLAN_BOOST; 64 65 /** 66 * App ID for server initiated requests, entitlement and activation. 67 */ 68 public static final String APP_ODSA_SERVER_INITIATED_REQUESTS = 69 Ts43Constants.APP_ODSA_SERVER_INITIATED_REQUESTS; 70 71 /** 72 * App ID for direct carrier billing. 73 */ 74 public static final String APP_DIRECT_CARRIER_BILLING = 75 Ts43Constants.APP_DIRECT_CARRIER_BILLING; 76 77 /** 78 * App ID for private user identity. 79 */ 80 public static final String APP_PRIVATE_USER_IDENTITY = Ts43Constants.APP_PRIVATE_USER_IDENTITY; 81 82 /** 83 * App ID for phone number information. 84 */ 85 public static final String APP_PHONE_NUMBER_INFORMATION = 86 Ts43Constants.APP_PHONE_NUMBER_INFORMATION; 87 88 /** 89 * App ID for satellite entitlement. 90 */ 91 public static final String APP_SATELLITE_ENTITLEMENT = Ts43Constants.APP_SATELLITE_ENTITLEMENT; 92 93 /** 94 * App ID for ODSA for Cross-TS.43 platform device, Entitlement and Activation. 95 */ 96 public static final String APP_ODSA_CROSS_TS43 = Ts43Constants.APP_ODSA_CROSS_TS43; 97 98 private final CarrierConfig carrierConfig; 99 private final EapAkaApi eapAkaApi; 100 @Nullable 101 private ServiceEntitlementRequest mOidcRequest; 102 /** 103 * Creates an instance for service entitlement configuration query and operation for the 104 * carrier. 105 * 106 * @param context context of application 107 * @param carrierConfig carrier specific configs used in the queries and operations. 108 * @param simSubscriptionId the subscription ID of the carrier's SIM on device. This indicates 109 * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA 110 * authentication with. See 111 * {@link android.telephony.SubscriptionManager} 112 * for how to get the subscription ID. 113 */ ServiceEntitlement(Context context, CarrierConfig carrierConfig, int simSubscriptionId)114 public ServiceEntitlement(Context context, CarrierConfig carrierConfig, int simSubscriptionId) { 115 this(context, carrierConfig, simSubscriptionId, /* saveHttpHistory= */ false); 116 } 117 118 /** 119 * Creates an instance for service entitlement configuration query and operation for the 120 * carrier. 121 * 122 * @param context context of application 123 * @param carrierConfig carrier specific configs used in the queries and operations. 124 * @param simSubscriptionId the subscription ID of the carrier's SIM on device. This indicates 125 * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA authentication with. See {@link 126 * android.telephony.SubscriptionManager} for how to get the subscription ID. 127 * @param saveHttpHistory set to {@code true} to save the history of request and response which 128 * can later be retrieved by {@code getHistory()}. Intended for debugging. 129 */ ServiceEntitlement( Context context, CarrierConfig carrierConfig, int simSubscriptionId, boolean saveHttpHistory)130 public ServiceEntitlement( 131 Context context, 132 CarrierConfig carrierConfig, 133 int simSubscriptionId, 134 boolean saveHttpHistory) { 135 this( 136 context, 137 carrierConfig, 138 simSubscriptionId, 139 saveHttpHistory, 140 DebugUtils.getBypassEapAkaResponse()); 141 } 142 143 /** 144 * Creates an instance for service entitlement configuration query and operation for the 145 * carrier. 146 * 147 * @param context context of application 148 * @param carrierConfig carrier specific configs used in the queries and operations. 149 * @param simSubscriptionId the subscription ID of the carrier's SIM on device. This indicates 150 * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA authentication with. See {@link 151 * android.telephony.SubscriptionManager} for how to get the subscription ID. 152 * @param saveHttpHistory set to {@code true} to save the history of request and response which 153 * can later be retrieved by {@code getHistory()}. Intended for debugging. 154 * @param bypassEapAkaResponse set to non empty string to bypass EAP-AKA authentication. 155 * The client will accept any challenge from the server and return this string as a 156 * response. Must not be {@code null}. Intended for testing. 157 */ ServiceEntitlement( Context context, CarrierConfig carrierConfig, int simSubscriptionId, boolean saveHttpHistory, String bypassEapAkaResponse)158 public ServiceEntitlement( 159 Context context, 160 CarrierConfig carrierConfig, 161 int simSubscriptionId, 162 boolean saveHttpHistory, 163 String bypassEapAkaResponse) { 164 this.carrierConfig = carrierConfig; 165 this.eapAkaApi = 166 new EapAkaApi(context, simSubscriptionId, saveHttpHistory, bypassEapAkaResponse); 167 } 168 169 @VisibleForTesting ServiceEntitlement(CarrierConfig carrierConfig, EapAkaApi eapAkaApi)170 ServiceEntitlement(CarrierConfig carrierConfig, EapAkaApi eapAkaApi) { 171 this.carrierConfig = carrierConfig; 172 this.eapAkaApi = eapAkaApi; 173 } 174 175 /** 176 * Retrieves service entitlement configuration. For on device service activation (ODSA) of eSIM 177 * for companion/primary devices, use {@link #performEsimOdsa} instead. 178 * 179 * <p>Supported {@code appId}: {@link #APP_VOLTE}, {@link #APP_VOWIFI}, {@link #APP_SMSOIP}. 180 * 181 * <p>This method sends an HTTP GET request to entitlement server, responds to EAP-AKA 182 * challenge if needed, and returns the raw configuration doc as a string. The following 183 * parameters are set in the HTTP request: 184 * 185 * <ul> 186 * <li>"app": {@code appId} 187 * <li>"vers": 0, or {@code request.configurationVersion()} if it's not 0. 188 * <li>"entitlement_version": "2.0", or {@code request.entitlementVersion()} if it's not empty. 189 * <li>"token": not set, or {@code request.authenticationToken()} if it's not empty. 190 * <li>"IMSI": if "token" is set, set to {@link android.telephony.TelephonyManager#getImei}. 191 * <li>"EAP_ID": if "token" is not set, set this parameter to trigger embedded EAP-AKA 192 * authentication as described in TS.43 section 2.6.1. Its value is derived from IMSI as per 193 * GSMA spec RCC.14 section C.2. 194 * <li>"terminal_id": IMEI, or {@code request.terminalId()} if it's not empty. 195 * <li>"terminal_vendor": {@link android.os.Build#MANUFACTURER}, or {@code 196 * request.terminalVendor()} if it's not empty. 197 * <li>"terminal_model": {@link android.os.Build#MODEL}, or {@code request.terminalModel()} if 198 * it's not empty. 199 * <li>"terminal_sw_version": {@link android.os.Build.VERSION#BASE_OS}, or {@code 200 * request.terminalSoftwareVersion()} if it's not empty. 201 * <li>"app_name": not set, or {@code request.appName()} if it's not empty. 202 * <li>"app_version": not set, or {@code request.appVersion()} if it's not empty. 203 * <li>"notif_token": not set, or {@code request.notificationToken()} if it's not empty. 204 * <li>"notif_action": {@code request.notificationAction()} if "notif_token" is set, otherwise 205 * not set. 206 * </ul> 207 * 208 * <p>Requires permission: READ_PRIVILEGED_PHONE_STATE, or carrier privilege. 209 * 210 * @param appId an app ID string defined in TS.43 section 2.2, e.g. {@link #APP_VOWIFI}. 211 * @param request contains parameters that can be used in the HTTP request. 212 */ 213 @NonNull queryEntitlementStatus(String appId, ServiceEntitlementRequest request)214 public String queryEntitlementStatus(String appId, ServiceEntitlementRequest request) 215 throws ServiceEntitlementException { 216 return queryEntitlementStatus(ImmutableList.of(appId), request); 217 } 218 219 /** 220 * Retrieves service entitlement configurations for multiple app IDs in one HTTP 221 * request/response. For on device service activation (ODSA) of eSIM for companion/primary 222 * devices, use {@link #performEsimOdsa} instead. 223 * 224 * <p>Same as {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)} except that 225 * multiple "app" parameters will be set in the HTTP request, in the order as they appear in 226 * parameter {@code appIds}. 227 */ 228 @NonNull queryEntitlementStatus( ImmutableList<String> appIds, ServiceEntitlementRequest request)229 public String queryEntitlementStatus( 230 ImmutableList<String> appIds, ServiceEntitlementRequest request) 231 throws ServiceEntitlementException { 232 return getEntitlementStatusResponse(appIds, request).body(); 233 } 234 235 /** 236 * Retrieves service entitlement configurations for multiple app IDs in one HTTP 237 * request/response. For on device service activation (ODSA) of eSIM for companion/primary 238 * devices, use {@link #performEsimOdsa} instead. 239 * 240 * <p>Same as {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)} except that 241 * multiple "app" parameters will be set in the HTTP request, in the order as they appear in 242 * parameter {@code appIds}. Additional parameters from {@code additionalHeaders} are set to the 243 * HTTP request. 244 */ 245 @NonNull queryEntitlementStatus( ImmutableList<String> appIds, ServiceEntitlementRequest request, ImmutableMap<String, String> additionalHeaders)246 public String queryEntitlementStatus( 247 ImmutableList<String> appIds, 248 ServiceEntitlementRequest request, 249 ImmutableMap<String, String> additionalHeaders) 250 throws ServiceEntitlementException { 251 return getEntitlementStatusResponse(appIds, request, additionalHeaders).body(); 252 } 253 254 /** 255 * Retrieves service entitlement configurations for multiple app IDs in one HTTP 256 * request/response. For on device service activation (ODSA) of eSIM for companion/primary 257 * devices, use {@link #performEsimOdsa} instead. 258 * 259 * <p>Same as {@link #queryEntitlementStatus(ImmutableList, ServiceEntitlementRequest)} 260 * except that it returns the full HTTP response instead of just the body. 261 */ 262 @NonNull getEntitlementStatusResponse(ImmutableList<String> appIds, ServiceEntitlementRequest request)263 public HttpResponse getEntitlementStatusResponse(ImmutableList<String> appIds, 264 ServiceEntitlementRequest request) 265 throws ServiceEntitlementException { 266 return getEntitlementStatusResponse(appIds, request, ImmutableMap.of()); 267 } 268 269 /** 270 * Retrieves service entitlement configurations for multiple app IDs in one HTTP 271 * request/response. For on device service activation (ODSA) of eSIM for companion/primary 272 * devices, use {@link #performEsimOdsa} instead. 273 */ 274 @NonNull getEntitlementStatusResponse( ImmutableList<String> appIds, ServiceEntitlementRequest request, ImmutableMap<String, String> additionalHeaders)275 public HttpResponse getEntitlementStatusResponse( 276 ImmutableList<String> appIds, 277 ServiceEntitlementRequest request, 278 ImmutableMap<String, String> additionalHeaders) 279 throws ServiceEntitlementException { 280 return eapAkaApi.queryEntitlementStatus(appIds, carrierConfig, request, additionalHeaders); 281 } 282 283 /** 284 * Performs on device service activation (ODSA) of eSIM for companion/primary devices. 285 * 286 * <p>Supported {@code appId}: {@link #APP_ODSA_COMPANION}, {@link #APP_ODSA_PRIMARY}. 287 * 288 * <p>Similar to {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)}, this method 289 * sends an HTTP GET request to entitlement server, responds to EAP-AKA challenge if needed, and 290 * returns the raw configuration doc as a string. Additional parameters from {@code operation} 291 * are set to the HTTP request. See {@link EsimOdsaOperation} for details. 292 */ 293 @NonNull performEsimOdsa( String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation)294 public String performEsimOdsa( 295 String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation) 296 throws ServiceEntitlementException { 297 return performEsimOdsa(appId, request, operation, ImmutableMap.of()); 298 } 299 300 /** 301 * Performs on device service activation (ODSA) of eSIM for companion/primary devices. 302 * 303 * <p>Supported {@code appId}: {@link #APP_ODSA_COMPANION}, {@link #APP_ODSA_PRIMARY}. 304 * 305 * <p>Similar to {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)}, this method 306 * sends an HTTP GET request to entitlement server, responds to EAP-AKA challenge if needed, and 307 * returns the raw configuration doc as a string. Additional parameters from {@code operation} 308 * are set to the HTTP request. See {@link EsimOdsaOperation} for details. Additional parameters 309 * from {@code additionalHeaders} are set to the HTTP request. 310 */ 311 @NonNull performEsimOdsa( String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation, ImmutableMap<String, String> additionalHeaders)312 public String performEsimOdsa( 313 String appId, 314 ServiceEntitlementRequest request, 315 EsimOdsaOperation operation, 316 ImmutableMap<String, String> additionalHeaders) 317 throws ServiceEntitlementException { 318 return getEsimOdsaResponse(appId, request, operation, additionalHeaders).body(); 319 } 320 321 /** 322 * Retrieves the HTTP response after performing on device service activation (ODSA) of eSIM for 323 * companion/primary devices. 324 * 325 * <p>Same as {@link #performEsimOdsa(String, ServiceEntitlementRequest, EsimOdsaOperation)} 326 * except that it returns the full HTTP response instead of just the body. 327 */ 328 @NonNull getEsimOdsaResponse( String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation)329 public HttpResponse getEsimOdsaResponse( 330 String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation) 331 throws ServiceEntitlementException { 332 return getEsimOdsaResponse(appId, request, operation, ImmutableMap.of()); 333 } 334 335 /** 336 * Retrieves the HTTP response after performing on device service activation (ODSA) of eSIM for 337 * companion/primary devices. 338 * 339 * <p>Same as {@link #performEsimOdsa(String, ServiceEntitlementRequest, EsimOdsaOperation)} 340 * except that it returns the full HTTP response instead of just the body. Additional parameters 341 * from {@code additionalHeaders} are set to the HTTP request. 342 */ 343 @NonNull getEsimOdsaResponse( String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation, ImmutableMap<String, String> additionalHeaders)344 public HttpResponse getEsimOdsaResponse( 345 String appId, 346 ServiceEntitlementRequest request, 347 EsimOdsaOperation operation, 348 ImmutableMap<String, String> additionalHeaders) 349 throws ServiceEntitlementException { 350 return eapAkaApi.performEsimOdsaOperation( 351 appId, carrierConfig, request, operation, additionalHeaders); 352 } 353 354 /** 355 * Retrieves the endpoint for OpenID Connect(OIDC) authentication. 356 * 357 * <p>Implementation based on section 2.8.2 of TS.43 358 * 359 * <p>The user should call {@link #queryEntitlementStatusFromOidc(String url)} with the 360 * authentication result to retrieve the service entitlement configuration. 361 * 362 * @param appId an app ID string defined in TS.43 section 2.2 363 * @param request contains parameters that can be used in the HTTP request 364 */ 365 @NonNull acquireOidcAuthenticationEndpoint(String appId, ServiceEntitlementRequest request)366 public String acquireOidcAuthenticationEndpoint(String appId, ServiceEntitlementRequest request) 367 throws ServiceEntitlementException { 368 return acquireOidcAuthenticationEndpoint(appId, request, ImmutableMap.of()); 369 } 370 371 /** 372 * Retrieves the endpoint for OpenID Connect(OIDC) authentication. 373 * 374 * <p>Implementation based on section 2.8.2 of TS.43 375 * 376 * <p>The user should call {@link #queryEntitlementStatusFromOidc(String url)} with the 377 * authentication result to retrieve the service entitlement configuration. 378 * 379 * @param appId an app ID string defined in TS.43 section 2.2 380 * @param request contains parameters that can be used in the HTTP request 381 * @param additionalHeaders additional headers to be set in the HTTP request 382 */ 383 @NonNull acquireOidcAuthenticationEndpoint( String appId, ServiceEntitlementRequest request, ImmutableMap<String, String> additionalHeaders)384 public String acquireOidcAuthenticationEndpoint( 385 String appId, 386 ServiceEntitlementRequest request, 387 ImmutableMap<String, String> additionalHeaders) 388 throws ServiceEntitlementException { 389 mOidcRequest = request; 390 return eapAkaApi.acquireOidcAuthenticationEndpoint( 391 appId, carrierConfig, request, additionalHeaders); 392 } 393 394 /** 395 * Retrieves the service entitlement configuration from OIDC authentication result. 396 * 397 * <p>Implementation based on section 2.8.2 of TS.43. 398 * 399 * <p>{@link #acquireOidcAuthenticationEndpoint} must be called before calling this method. 400 * 401 * @param url the redirect url from OIDC authentication result. 402 */ 403 @NonNull queryEntitlementStatusFromOidc(String url)404 public String queryEntitlementStatusFromOidc(String url) throws ServiceEntitlementException { 405 return getEntitlementStatusResponseFromOidc(url).body(); 406 } 407 408 /** 409 * Retrieves the HTTP response containing the service entitlement configuration from 410 * OIDC authentication result. 411 * 412 * <p>Same as {@link #queryEntitlementStatusFromOidc(String)} except that it returns the 413 * full HTTP response instead of just the body. 414 * 415 * @param url the redirect url from OIDC authentication result. 416 */ 417 @NonNull getEntitlementStatusResponseFromOidc(String url)418 public HttpResponse getEntitlementStatusResponseFromOidc(String url) 419 throws ServiceEntitlementException { 420 return getEntitlementStatusResponseFromOidc(url, ImmutableMap.of()); 421 } 422 423 /** 424 * Retrieves the HTTP response containing the service entitlement configuration from OIDC 425 * authentication result. 426 * 427 * <p>Same as {@link #queryEntitlementStatusFromOidc(String)} except that it returns the full 428 * HTTP response instead of just the body. 429 * 430 * @param url the redirect url from OIDC authentication result. 431 * @param additionalHeaders additional headers to be set in the HTTP request 432 */ 433 @NonNull getEntitlementStatusResponseFromOidc( String url, ImmutableMap<String, String> additionalHeaders)434 public HttpResponse getEntitlementStatusResponseFromOidc( 435 String url, ImmutableMap<String, String> additionalHeaders) 436 throws ServiceEntitlementException { 437 if (mOidcRequest == null) { 438 throw new IllegalStateException( 439 "acquireOidcAuthenticationEndpoint must be called before calling this method."); 440 } 441 return eapAkaApi.queryEntitlementStatusFromOidc( 442 url, carrierConfig, mOidcRequest, additionalHeaders); 443 } 444 445 /** 446 * Retrieves the history of past HTTP request and responses if {@code saveHttpHistory} was set 447 * in constructor. 448 */ 449 @NonNull getHistory()450 public List<String> getHistory() { 451 return eapAkaApi.getHistory(); 452 } 453 454 /** 455 * Clears the history of past HTTP request and responses. 456 */ clearHistory()457 public void clearHistory() { 458 eapAkaApi.clearHistory(); 459 } 460 } 461