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