• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.imsserviceentitlement;
18 
19 import static com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior.NEEDS_TO_RESET;
20 import static com.android.imsserviceentitlement.entitlement.EntitlementConfiguration.ClientBehavior.VALID_DURING_VALIDITY;
21 import static com.android.libraries.entitlement.ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29 
30 import android.content.Context;
31 import android.os.PersistableBundle;
32 import android.telephony.CarrierConfigManager;
33 import android.telephony.SubscriptionManager;
34 
35 import androidx.test.core.app.ApplicationProvider;
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import com.android.imsserviceentitlement.entitlement.EntitlementConfiguration;
39 import com.android.imsserviceentitlement.entitlement.EntitlementResult;
40 import com.android.imsserviceentitlement.fcm.FcmTokenStore;
41 import com.android.imsserviceentitlement.utils.TelephonyUtils;
42 import com.android.libraries.entitlement.ServiceEntitlement;
43 import com.android.libraries.entitlement.ServiceEntitlementException;
44 import com.android.libraries.entitlement.ServiceEntitlementRequest;
45 
46 import com.google.common.collect.ImmutableList;
47 
48 import org.junit.Before;
49 import org.junit.Rule;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.Mock;
53 import org.mockito.Spy;
54 import org.mockito.junit.MockitoJUnit;
55 import org.mockito.junit.MockitoRule;
56 
57 import java.text.SimpleDateFormat;
58 import java.time.Clock;
59 import java.time.Instant;
60 import java.time.ZoneOffset;
61 import java.util.Date;
62 import java.util.Locale;
63 import java.util.TimeZone;
64 
65 @RunWith(AndroidJUnit4.class)
66 public class ImsEntitlementApiTest {
67     @Rule public final MockitoRule rule = MockitoJUnit.rule();
68 
69     @Spy private Context mContext = ApplicationProvider.getApplicationContext();
70 
71     @Mock private ServiceEntitlement mMockServiceEntitlement;
72     @Mock private EntitlementConfiguration mMockEntitlementConfiguration;
73     @Mock private CarrierConfigManager mCarrierConfigManager;
74 
75     private static final int SUB_ID = 1;
76     private static final String FCM_TOKEN = "FCM_TOKEN";
77     private static final String RAW_XML =
78             "<wap-provisioningdoc version=\"1.1\">"
79                     + "  <characteristic type=\"VERS\">"
80                     + "    <parm name=\"version\" value=\"1\"/>"
81                     + "    <parm name=\"validity\" value=\"1728000\"/>"
82                     + "  </characteristic>"
83                     + "  <characteristic type=\"TOKEN\">"
84                     + "    <parm name=\"token\" value=\"kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX\"/>"
85                     + "    <parm name=\"validity\" value=\"3600\"/>"
86                     + "  </characteristic>"
87                     + "  <characteristic type=\"APPLICATION\">"
88                     + "    <parm name=\"AppID\" value=\"ap2004\"/>"
89                     + "    <parm name=\"EntitlementStatus\" value=\"1\"/>"
90                     + "  </characteristic>"
91                     + "</wap-provisioningdoc>";
92     private static final String RAW_XML_NEW_TOKEN =
93             "<wap-provisioningdoc version=\"1.1\">"
94                     + "  <characteristic type=\"VERS\">"
95                     + "    <parm name=\"version\" value=\"1\"/>"
96                     + "    <parm name=\"validity\" value=\"1728000\"/>"
97                     + "  </characteristic>"
98                     + "  <characteristic type=\"TOKEN\">"
99                     + "    <parm name=\"token\" value=\"NEW_TOKEN\"/>"
100                     + "    <parm name=\"validity\" value=\"3600\"/>"
101                     + "  </characteristic>\n"
102                     + "  <characteristic type=\"APPLICATION\">"
103                     + "    <parm name=\"AppID\" value=\"ap2004\"/>"
104                     + "    <parm name=\"EntitlementStatus\" value=\"1\"/>"
105                     + "  </characteristic>"
106                     + "</wap-provisioningdoc>";
107 
108     private static final String MULTIPLE_APPIDS_RAW_XML =
109             "<wap-provisioningdoc version=\"1.1\">"
110                     + "  <characteristic type=\"VERS\">"
111                     + "    <parm name=\"version\" value=\"1\"/>"
112                     + "    <parm name=\"validity\" value=\"1728000\"/>"
113                     + "  </characteristic>"
114                     + "  <characteristic type=\"TOKEN\">"
115                     + "    <parm name=\"token\" value=\"kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX\"/>"
116                     + "    <parm name=\"validity\" value=\"3600\"/>"
117                     + "  </characteristic>"
118                     + "  <characteristic type=\"APPLICATION\">"
119                     + "    <parm name=\"AppID\" value=\"ap2003\"/>"
120                     + "    <parm name=\"EntitlementStatus\" value=\"1\"/>"
121                     + "  </characteristic>\n"
122                     + "  <characteristic type=\"APPLICATION\">"
123                     + "    <parm name=\"AppID\" value=\"ap2004\"/>\n"
124                     + "    <parm name=\"EntitlementStatus\" value=\"1\"/>"
125                     + "  </characteristic>"
126                     + "  <characteristic type=\"APPLICATION\">"
127                     + "    <parm name=\"AppID\" value=\"ap2005\"/>"
128                     + "    <parm name=\"EntitlementStatus\" value=\"1\"/>"
129                     + "  </characteristic>"
130                     + "</wap-provisioningdoc>";
131 
132     private final EntitlementConfiguration mEntitlementConfiguration =
133             new EntitlementConfiguration(ApplicationProvider.getApplicationContext(), SUB_ID);
134 
135     private ImsEntitlementApi mImsEntitlementApi;
136 
137     @Before
setUp()138     public void setUp() {
139         setImsProvisioningBool(true);
140         FcmTokenStore.setToken(mContext, SUB_ID, FCM_TOKEN);
141         mEntitlementConfiguration.reset();
142     }
143 
144     @Test
checkEntitlementStatus_verifyVowifiStatus()145     public void checkEntitlementStatus_verifyVowifiStatus() throws Exception {
146         setImsProvisioningBool(false);
147         setupImsEntitlementApi(mEntitlementConfiguration);
148         when(mMockServiceEntitlement.queryEntitlementStatus(
149                 eq(ImmutableList.of(ServiceEntitlement.APP_VOWIFI)), any())).thenReturn(RAW_XML);
150 
151         EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
152 
153         assertThat(result.getVowifiStatus().vowifiEntitled()).isTrue();
154     }
155 
156     @Test
checkEntitlementStatus_verifyImsAppsStatus()157     public void checkEntitlementStatus_verifyImsAppsStatus() throws Exception {
158         setupImsEntitlementApi(mEntitlementConfiguration);
159         when(mMockServiceEntitlement.queryEntitlementStatus(
160                 eq(ImmutableList.of(
161                         ServiceEntitlement.APP_VOWIFI,
162                         ServiceEntitlement.APP_VOLTE,
163                         ServiceEntitlement.APP_SMSOIP)), any())
164         ).thenReturn(MULTIPLE_APPIDS_RAW_XML);
165 
166         EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
167 
168         assertThat(result.getVowifiStatus().vowifiEntitled()).isTrue();
169         assertThat(result.getVolteStatus().isActive()).isTrue();
170         assertThat(result.getSmsoveripStatus().isActive()).isTrue();
171     }
172 
173     @Test
checkEntitlementStatus_verifyConfigs()174     public void checkEntitlementStatus_verifyConfigs() throws Exception {
175         setImsProvisioningBool(false);
176         setupImsEntitlementApi(mEntitlementConfiguration);
177         when(mMockServiceEntitlement.queryEntitlementStatus(
178                 eq(ImmutableList.of(ServiceEntitlement.APP_VOWIFI)),
179                 any())).thenReturn(RAW_XML);
180 
181         EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
182 
183         assertThat(mEntitlementConfiguration.getVoWifiStatus()).isEqualTo(1);
184         assertThat(mEntitlementConfiguration.getVolteStatus()).isEqualTo(2);
185         assertThat(mEntitlementConfiguration.getSmsOverIpStatus()).isEqualTo(2);
186         assertThat(mEntitlementConfiguration.getToken().get()).isEqualTo(
187                 "kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX");
188         assertThat(mEntitlementConfiguration.getTokenValidity()).isEqualTo(3600);
189         assertThat(mEntitlementConfiguration.entitlementValidation()).isEqualTo(
190                 VALID_DURING_VALIDITY);
191     }
192 
193     @Test
checkEntitlementStatus_resultNull_verifyVowifiStatusAndConfigs()194     public void checkEntitlementStatus_resultNull_verifyVowifiStatusAndConfigs() throws Exception {
195         setImsProvisioningBool(false);
196         setupImsEntitlementApi(mEntitlementConfiguration);
197         when(mMockServiceEntitlement.queryEntitlementStatus(
198                 eq(ImmutableList.of(ServiceEntitlement.APP_VOWIFI)), any())).thenReturn(null);
199 
200         EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
201 
202         assertThat(result.getVowifiStatus().vowifiEntitled()).isFalse();
203         assertThat(mEntitlementConfiguration.getVoWifiStatus()).isEqualTo(2);
204         assertThat(mEntitlementConfiguration.getVolteStatus()).isEqualTo(2);
205         assertThat(mEntitlementConfiguration.getSmsOverIpStatus()).isEqualTo(2);
206         assertThat(mEntitlementConfiguration.getToken().isPresent()).isFalse();
207         assertThat(mEntitlementConfiguration.getTokenValidity()).isEqualTo(0);
208         assertThat(mEntitlementConfiguration.entitlementValidation()).isEqualTo(NEEDS_TO_RESET);
209     }
210 
211     @Test
checkEntitlementStatus_httpResponse511_dataStoreReset()212     public void checkEntitlementStatus_httpResponse511_dataStoreReset() throws Exception {
213         setImsProvisioningBool(false);
214         setupImsEntitlementApi(mMockEntitlementConfiguration);
215         when(mMockServiceEntitlement.queryEntitlementStatus(
216                 eq(ImmutableList.of(ServiceEntitlement.APP_VOWIFI)), any()))
217                 .thenThrow(
218                         new ServiceEntitlementException(
219                                 ERROR_HTTP_STATUS_NOT_SUCCESS, 511, "Invalid connection response"));
220 
221         EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
222 
223         verify(mMockEntitlementConfiguration).reset();
224         assertThat(result).isNull();
225     }
226 
227     @Test
checkEntitlementStatus_httpResponse511_fullAuthnDone()228     public void checkEntitlementStatus_httpResponse511_fullAuthnDone() throws Exception {
229         setImsProvisioningBool(false);
230         setupImsEntitlementApi(mEntitlementConfiguration);
231         mEntitlementConfiguration.update(RAW_XML);
232         // While perform fast-authn, throws exception with code 511
233         when(mMockServiceEntitlement.queryEntitlementStatus(
234                 ImmutableList.of(ServiceEntitlement.APP_VOWIFI),
235                 authenticationRequest("kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX")))
236                 .thenThrow(
237                         new ServiceEntitlementException(
238                                 ERROR_HTTP_STATUS_NOT_SUCCESS, 511, "Invalid connection response"));
239         // While perform full-authn, return the result
240         when(mMockServiceEntitlement.queryEntitlementStatus(
241                 ImmutableList.of(ServiceEntitlement.APP_VOWIFI),
242                 authenticationRequest(null)))
243                 .thenReturn(RAW_XML_NEW_TOKEN);
244 
245         EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
246 
247         assertThat(result).isNotNull();
248         assertThat(mEntitlementConfiguration.getToken().get()).isEqualTo("NEW_TOKEN");
249     }
250 
251     @Test
checkEntitlementStatus_httpResponse503WithDateTime_returnsRetryAfter()252     public void checkEntitlementStatus_httpResponse503WithDateTime_returnsRetryAfter()
253             throws Exception {
254         setImsProvisioningBool(false);
255         setupImsEntitlementApi(mEntitlementConfiguration);
256         mEntitlementConfiguration.update(RAW_XML);
257         Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(0), ZoneOffset.UTC);
258         ImsEntitlementApi.sClock = fixedClock;
259 
260         // While perform fast-authn, throws exception with code 503
261         when(mMockServiceEntitlement.queryEntitlementStatus(
262                 ImmutableList.of(ServiceEntitlement.APP_VOWIFI),
263                 authenticationRequest("kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX")))
264                 .thenThrow(
265                         new ServiceEntitlementException(
266                                 ERROR_HTTP_STATUS_NOT_SUCCESS,
267                                 503,
268                                 getDateTimeAfter(120, fixedClock),
269                                 "Invalid connection response"));
270 
271         EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
272 
273         assertThat(result).isNotNull();
274         assertThat(result.getRetryAfterSeconds()).isEqualTo(120);
275     }
276 
277     @Test
checkEntitlementStatus_httpResponse503WithNumericValue_returnsRetryAfter()278     public void checkEntitlementStatus_httpResponse503WithNumericValue_returnsRetryAfter()
279             throws Exception {
280         setImsProvisioningBool(false);
281         setupImsEntitlementApi(mEntitlementConfiguration);
282         mEntitlementConfiguration.update(RAW_XML);
283         // While perform fast-authn, throws exception with code 503
284         when(mMockServiceEntitlement.queryEntitlementStatus(
285                 ImmutableList.of(ServiceEntitlement.APP_VOWIFI),
286                 authenticationRequest("kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX")))
287                 .thenThrow(
288                         new ServiceEntitlementException(
289                                 ERROR_HTTP_STATUS_NOT_SUCCESS,
290                                 503,
291                                 "120",
292                                 "Invalid connection response"));
293 
294         EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
295 
296         assertThat(result).isNotNull();
297         assertThat(result.getRetryAfterSeconds()).isEqualTo(120);
298     }
299 
300     @Test
checkEntitlementStatus_invalidSubId_resultNull()301     public void checkEntitlementStatus_invalidSubId_resultNull() {
302         ImsEntitlementApi imsEntitlementApi =
303                 new ImsEntitlementApi(mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
304 
305         EntitlementResult result = imsEntitlementApi.checkEntitlementStatus();
306 
307         assertThat(result).isNull();
308     }
309 
authenticationRequest(String token)310     private ServiceEntitlementRequest authenticationRequest(String token) {
311         ServiceEntitlementRequest.Builder requestBuilder = ServiceEntitlementRequest.builder();
312         if (token != null) {
313             requestBuilder.setAuthenticationToken(token);
314         }
315         requestBuilder.setNotificationToken(FcmTokenStore.getToken(mContext, SUB_ID));
316         requestBuilder.setTerminalVendor("vendorX");
317         requestBuilder.setTerminalModel("modelY");
318         requestBuilder.setTerminalSoftwareVersion("versionZ");
319         requestBuilder.setAcceptContentType(ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_XML);
320         return requestBuilder.build();
321     }
322 
setupImsEntitlementApi(EntitlementConfiguration entitlementConfiguration)323     private void setupImsEntitlementApi(EntitlementConfiguration entitlementConfiguration) {
324         mImsEntitlementApi = new ImsEntitlementApi(
325                 mContext,
326                 SUB_ID,
327                 TelephonyUtils.isImsProvisioningRequired(mContext, SUB_ID),
328                 mMockServiceEntitlement,
329                 entitlementConfiguration);
330     }
331 
setImsProvisioningBool(boolean provisioning)332     private void setImsProvisioningBool(boolean provisioning) {
333         PersistableBundle carrierConfig = new PersistableBundle();
334         carrierConfig.putBoolean(
335                 CarrierConfigManager.ImsServiceEntitlement.KEY_IMS_PROVISIONING_BOOL,
336                 provisioning
337         );
338         when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(carrierConfig);
339         when(mContext.getSystemService(CarrierConfigManager.class))
340                 .thenReturn(mCarrierConfigManager);
341     }
342 
getDateTimeAfter(long seconds, Clock fixedClock)343     private String getDateTimeAfter(long seconds, Clock fixedClock) {
344         SimpleDateFormat dateFormat = new SimpleDateFormat(
345                 "EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
346         dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
347         return dateFormat.format(Date.from(fixedClock.instant().plusSeconds(seconds)));
348     }
349 }
350