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