1 /*
2  * Copyright 2020 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.internal.telephony;
18 
19 import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY;
20 
21 import static junit.framework.Assert.assertEquals;
22 import static junit.framework.Assert.assertTrue;
23 import static junit.framework.Assert.fail;
24 
25 import static org.mockito.Matchers.any;
26 import static org.mockito.Matchers.anyInt;
27 import static org.mockito.Matchers.eq;
28 import static org.mockito.Mockito.atLeastOnce;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
34 
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.ServiceConnection;
39 import android.net.Uri;
40 import android.os.Handler;
41 import android.os.IBinder;
42 import android.os.Looper;
43 import android.os.RemoteException;
44 import android.telephony.IBootstrapAuthenticationCallback;
45 import android.telephony.TelephonyManager;
46 import android.telephony.gba.GbaAuthRequest;
47 import android.telephony.gba.GbaService;
48 import android.telephony.gba.IGbaService;
49 import android.telephony.gba.UaSecurityProtocolIdentifier;
50 import android.testing.AndroidTestingRunner;
51 import android.testing.TestableLooper;
52 import android.util.Log;
53 
54 import androidx.test.filters.SmallTest;
55 
56 import com.android.internal.telephony.metrics.RcsStats;
57 
58 import org.junit.After;
59 import org.junit.Before;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.ArgumentCaptor;
63 
64 /**
65  * Unit tests for GbaManager
66  */
67 @RunWith(AndroidTestingRunner.class)
68 @TestableLooper.RunWithLooper
69 public final class GbaManagerTest {
70     private static final String LOG_TAG = "GbaManagerTest";
71 
72     private static final ComponentName TEST_DEFAULT_SERVICE_NAME = new ComponentName(
73             "TestGbaPkg", "TestGbaService");
74     private static final ComponentName TEST_SERVICE2_NAME = new ComponentName(
75             "TestGbaPkg2", "TestGbaService2");
76     private static final int RELEASE_NEVER = -1;
77     private static final int RELEASE_IMMEDIATELY = 0;
78     private static final int RELEASE_TIME_60S = 60 * 1000;
79     private static final int TEST_SUB_ID = Integer.MAX_VALUE;
80 
81     // Mocked classes
82     Context mMockContext;
83     IBinder mMockBinder;
84     IGbaService mMockGbaServiceBinder;
85     IBootstrapAuthenticationCallback mMockCallback;
86     RcsStats mMockRcsStats;
87 
88     private GbaManager mTestGbaManager;
89     private Handler mHandler;
90     private TestableLooper mLooper;
91 
92     @Before
setUp()93     public void setUp() throws Exception {
94         log("setUp");
95         mMockContext = mock(Context.class);
96         mMockBinder = mock(IBinder.class);
97         mMockGbaServiceBinder = mock(IGbaService.class);
98         mMockCallback = mock(IBootstrapAuthenticationCallback.class);
99         mMockRcsStats = mock(RcsStats.class);
100         if (Looper.myLooper() == null) {
101             Looper.prepare();
102         }
103         when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
104         when(mMockGbaServiceBinder.asBinder()).thenReturn(mMockBinder);
105         mTestGbaManager = new GbaManager(mMockContext, TEST_SUB_ID, null, 0, mMockRcsStats);
106         mHandler = mTestGbaManager.getHandler();
107         try {
108             mLooper = new TestableLooper(mHandler.getLooper());
109         } catch (Exception e) {
110             fail("Unable to create looper from handler.");
111         }
112     }
113 
114     @After
tearDown()115     public void tearDown() throws Exception {
116         log("tearDown");
117         mTestGbaManager.destroy();
118         mTestGbaManager = null;
119         mHandler = null;
120         mLooper.destroy();
121         mLooper = null;
122     }
123 
124     @Test
125     @SmallTest
testFailOnRequest()126     public void testFailOnRequest() throws Exception {
127         GbaAuthRequest request = createDefaultRequest();
128 
129         mTestGbaManager.bootstrapAuthenticationRequest(request);
130         mLooper.processAllMessages();
131 
132         verify(mMockContext, never()).bindService(any(), any(), anyInt());
133         verify(mMockCallback).onAuthenticationFailure(anyInt(), anyInt());
134         assertTrue(!mTestGbaManager.isServiceConnected());
135     }
136 
137     @Test
138     @SmallTest
testBindServiceOnRequest()139     public void testBindServiceOnRequest() throws Exception {
140         mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
141         GbaAuthRequest request = createDefaultRequest();
142 
143         mTestGbaManager.bootstrapAuthenticationRequest(request);
144         mLooper.processAllMessages();
145         bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
146         mLooper.processAllMessages();
147 
148         verify(mMockGbaServiceBinder).authenticationRequest(any());
149         assertTrue(mTestGbaManager.isServiceConnected());
150     }
151 
152     @Test
153     @SmallTest
testFailAndRetryOnRequest()154     public void testFailAndRetryOnRequest() throws RemoteException {
155         when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(false);
156         mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
157         GbaAuthRequest request = createDefaultRequest();
158 
159         mTestGbaManager.bootstrapAuthenticationRequest(request);
160 
161         for (int i = 0; i < GbaManager.MAX_RETRY; i++) {
162             mLooper.processAllMessages();
163             verify(mMockContext, times(i + 1)).bindService(any(), any(), anyInt());
164             try {
165                 Thread.sleep(GbaManager.RETRY_TIME_MS + 500);
166             } catch (InterruptedException e) {
167             }
168         }
169         assertTrue(!mTestGbaManager.isServiceConnected());
170         mLooper.processAllMessages();
171         verify(mMockCallback).onAuthenticationFailure(anyInt(), anyInt());
172     }
173 
174     @Test
175     @SmallTest
testBindServiceWhenPackageNameChanged()176     public void testBindServiceWhenPackageNameChanged() {
177         mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
178         mTestGbaManager.overrideReleaseTime(RELEASE_TIME_60S);
179         GbaAuthRequest request = createDefaultRequest();
180 
181         mTestGbaManager.bootstrapAuthenticationRequest(request);
182         mLooper.processAllMessages();
183         ServiceConnection conn = bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
184         mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName());
185 
186         assertEquals(TEST_SERVICE2_NAME.getPackageName(), mTestGbaManager.getServicePackage());
187 
188         mLooper.processAllMessages();
189         unbindService(conn);
190         bindAndConnectService(TEST_SERVICE2_NAME);
191         assertTrue(mTestGbaManager.isServiceConnected());
192     }
193 
194     @Test
195     @SmallTest
testBindServiceWhenReleaseTimeChanged()196     public void testBindServiceWhenReleaseTimeChanged() {
197         mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
198         mTestGbaManager.overrideReleaseTime(RELEASE_NEVER);
199 
200         assertEquals(RELEASE_NEVER, mTestGbaManager.getReleaseTime());
201         mLooper.processAllMessages();
202         bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
203 
204         assertTrue(mTestGbaManager.isServiceConnected());
205     }
206 
207     @Test
208     @SmallTest
testDontBindServiceWhenPackageNameChanged()209     public void testDontBindServiceWhenPackageNameChanged() {
210         mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName());
211 
212         mLooper.processAllMessages();
213 
214         verify(mMockContext, never()).bindService(any(), any(), anyInt());
215         assertTrue(!mTestGbaManager.isServiceConnected());
216     }
217 
218     @Test
219     @SmallTest
testDontBindServiceWhenReleaseTimeChanged()220     public void testDontBindServiceWhenReleaseTimeChanged() {
221         mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
222         mTestGbaManager.overrideReleaseTime(RELEASE_TIME_60S);
223 
224         mLooper.processAllMessages();
225 
226         verify(mMockContext, never()).bindService(any(), any(), anyInt());
227         assertTrue(!mTestGbaManager.isServiceConnected());
228     }
229 
230     @Test
231     @SmallTest
testMetricsGbaEvent()232     public void testMetricsGbaEvent() throws Exception {
233         mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
234         mTestGbaManager.overrideReleaseTime(RELEASE_NEVER);
235 
236         mLooper.processAllMessages();
237         bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
238         GbaAuthRequest request = createDefaultRequest();
239 
240         // Failure case
241         mTestGbaManager.bootstrapAuthenticationRequest(request);
242         mLooper.processAllMessages();
243 
244         ArgumentCaptor<GbaAuthRequest> captor = ArgumentCaptor.forClass(GbaAuthRequest.class);
245         verify(mMockGbaServiceBinder, times(1)).authenticationRequest(captor.capture());
246 
247         GbaAuthRequest capturedRequest = captor.getValue();
248         IBootstrapAuthenticationCallback callback = capturedRequest.getCallback();
249         callback.onAuthenticationFailure(capturedRequest.getToken(),
250                 GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY);
251 
252         verify(mMockRcsStats).onGbaFailureEvent(anyInt(),
253                 eq(GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY));
254 
255         // Success case
256         mTestGbaManager.bootstrapAuthenticationRequest(request);
257         mLooper.processAllMessages();
258 
259         ArgumentCaptor<GbaAuthRequest> captor2 = ArgumentCaptor.forClass(GbaAuthRequest.class);
260         verify(mMockGbaServiceBinder, times(2)).authenticationRequest(captor2.capture());
261 
262         GbaAuthRequest capturedRequest2 = captor2.getValue();
263         IBootstrapAuthenticationCallback callback2 = capturedRequest2.getCallback();
264         callback2.onKeysAvailable(capturedRequest2.getToken(), "".getBytes(), "");
265 
266         verify(mMockRcsStats).onGbaSuccessEvent(anyInt());
267     }
268 
bindAndConnectService(ComponentName component)269     private ServiceConnection bindAndConnectService(ComponentName component) {
270         ServiceConnection connection = bindService(component);
271         IGbaService.Stub serviceStub = mock(IGbaService.Stub.class);
272         when(mMockBinder.isBinderAlive()).thenReturn(true);
273         when(serviceStub.queryLocalInterface(any())).thenReturn(mMockGbaServiceBinder);
274         connection.onServiceConnected(component, serviceStub);
275         return connection;
276     }
277 
bindService(ComponentName component)278     private ServiceConnection bindService(ComponentName component) {
279         ArgumentCaptor<Intent> intentCaptor =
280                 ArgumentCaptor.forClass(Intent.class);
281         ArgumentCaptor<ServiceConnection> serviceCaptor =
282                 ArgumentCaptor.forClass(ServiceConnection.class);
283         verify(mMockContext, atLeastOnce()).bindService(intentCaptor.capture(),
284                 serviceCaptor.capture(), eq(
285                         Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE));
286         Intent testIntent = intentCaptor.getValue();
287         assertEquals(GbaService.SERVICE_INTERFACE, testIntent.getAction());
288         assertEquals(component.getPackageName(), testIntent.getPackage());
289         return serviceCaptor.getValue();
290     }
291 
unbindService(ServiceConnection conn)292     private void unbindService(ServiceConnection conn) {
293         verify(mMockContext).unbindService(eq(conn));
294     }
295 
createDefaultRequest()296     private GbaAuthRequest createDefaultRequest() {
297         final String naf = "3GPP-bootstrapping@naf1.operator.com";
298         final UaSecurityProtocolIdentifier.Builder builder =
299                 new UaSecurityProtocolIdentifier.Builder();
300         builder.setOrg(UaSecurityProtocolIdentifier.ORG_3GPP).setProtocol(
301                 UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS);
302         return new GbaAuthRequest(TEST_SUB_ID, TelephonyManager.APPTYPE_USIM,
303                 Uri.parse(naf), builder.build().toByteArray(), true, mMockCallback);
304     }
305 
log(String str)306     private void log(String str) {
307         Log.d(LOG_TAG, str);
308     }
309 }
310