TelecomSystemTest.java revision 92694519535c54f542b4ef3973e9c1934f2feeff
1/* 2 * Copyright (C) 2015 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 17package com.android.server.telecom.tests; 18 19import static org.mockito.Matchers.any; 20import static org.mockito.Matchers.anyBoolean; 21import static org.mockito.Matchers.anyInt; 22import static org.mockito.Matchers.anyString; 23import static org.mockito.Matchers.eq; 24import static org.mockito.Mockito.mock; 25import static org.mockito.Mockito.timeout; 26import static org.mockito.Mockito.verify; 27import static org.mockito.Mockito.when; 28 29import android.content.BroadcastReceiver; 30import android.content.ComponentName; 31import android.content.Context; 32import android.content.Intent; 33import android.net.Uri; 34import android.os.Bundle; 35import android.os.Handler; 36import android.os.UserHandle; 37import android.telecom.ConnectionRequest; 38import android.telecom.DisconnectCause; 39import android.telecom.ParcelableCall; 40import android.telecom.PhoneAccount; 41import android.telecom.PhoneAccountHandle; 42import android.telecom.TelecomManager; 43import android.telephony.TelephonyManager; 44 45import com.android.internal.telecom.IInCallAdapter; 46import com.android.internal.telephony.CallerInfo; 47import com.android.internal.telephony.CallerInfoAsyncQuery; 48import com.android.server.telecom.CallerInfoAsyncQueryFactory; 49import com.android.server.telecom.CallState; 50import com.android.server.telecom.CallsManager; 51import com.android.server.telecom.HeadsetMediaButton; 52import com.android.server.telecom.HeadsetMediaButtonFactory; 53import com.android.server.telecom.InCallWakeLockController; 54import com.android.server.telecom.InCallWakeLockControllerFactory; 55import com.android.server.telecom.Log; 56import com.android.server.telecom.MissedCallNotifier; 57import com.android.server.telecom.ProximitySensorManager; 58import com.android.server.telecom.ProximitySensorManagerFactory; 59import com.android.server.telecom.TelecomSystem; 60 61import org.mockito.ArgumentCaptor; 62import org.mockito.Mock; 63 64import java.util.concurrent.BrokenBarrierException; 65import java.util.concurrent.CountDownLatch; 66import java.util.concurrent.CyclicBarrier; 67 68public class TelecomSystemTest extends TelecomTestCase { 69 70 static final int TEST_TIMEOUT = 1000; // milliseconds 71 72 @Mock MissedCallNotifier mMissedCallNotifier; 73 @Mock HeadsetMediaButton mHeadsetMediaButton; 74 @Mock ProximitySensorManager mProximitySensorManager; 75 @Mock InCallWakeLockController mInCallWakeLockController; 76 77 final ComponentName mInCallServiceComponentNameX = 78 new ComponentName( 79 "incall-service-package-X", 80 "incall-service-class-X"); 81 final ComponentName mInCallServiceComponentNameY = 82 new ComponentName( 83 "incall-service-package-Y", 84 "incall-service-class-Y"); 85 86 InCallServiceFixture mInCallServiceFixtureX; 87 InCallServiceFixture mInCallServiceFixtureY; 88 89 final ComponentName mConnectionServiceComponentNameA = 90 new ComponentName( 91 "connection-service-package-A", 92 "connection-service-class-A"); 93 final ComponentName mConnectionServiceComponentNameB = 94 new ComponentName( 95 "connection-service-package-B", 96 "connection-service-class-B"); 97 98 final PhoneAccount mPhoneAccountA0 = 99 PhoneAccount.builder( 100 new PhoneAccountHandle( 101 mConnectionServiceComponentNameA, 102 "id A 0"), 103 "Phone account service A ID 0") 104 .addSupportedUriScheme("tel") 105 .setCapabilities( 106 PhoneAccount.CAPABILITY_CALL_PROVIDER | 107 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) 108 .build(); 109 final PhoneAccount mPhoneAccountA1 = 110 PhoneAccount.builder( 111 new PhoneAccountHandle( 112 mConnectionServiceComponentNameA, 113 "id A 1"), 114 "Phone account service A ID 1") 115 .addSupportedUriScheme("tel") 116 .setCapabilities( 117 PhoneAccount.CAPABILITY_CALL_PROVIDER | 118 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) 119 .build(); 120 final PhoneAccount mPhoneAccountB0 = 121 PhoneAccount.builder( 122 new PhoneAccountHandle( 123 mConnectionServiceComponentNameA, 124 "id B 0"), 125 "Phone account service B ID 0") 126 .addSupportedUriScheme("tel") 127 .setCapabilities( 128 PhoneAccount.CAPABILITY_CALL_PROVIDER | 129 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) 130 .build(); 131 132 ConnectionServiceFixture mConnectionServiceFixtureA; 133 ConnectionServiceFixture mConnectionServiceFixtureB; 134 135 CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture; 136 137 TelecomSystem mTelecomSystem; 138 139 @Override 140 public void setUp() throws Exception { 141 super.setUp(); 142 143 // First set up information about the In-Call services in the mock Context, since 144 // Telecom will search for these as soon as it is instantiated 145 setupInCallServices(); 146 147 // Next, create the TelecomSystem, our system under test 148 setupTelecomSystem(); 149 150 // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the 151 // now-running TelecomSystem 152 setupConnectionServices(); 153 } 154 155 @Override 156 public void tearDown() throws Exception { 157 mTelecomSystem = null; 158 super.tearDown(); 159 } 160 161 private void setupTelecomSystem() throws Exception { 162 HeadsetMediaButtonFactory headsetMediaButtonFactory = 163 mock(HeadsetMediaButtonFactory.class); 164 ProximitySensorManagerFactory proximitySensorManagerFactory = 165 mock(ProximitySensorManagerFactory.class); 166 InCallWakeLockControllerFactory inCallWakeLockControllerFactory = 167 mock(InCallWakeLockControllerFactory.class); 168 169 mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture(); 170 171 when(headsetMediaButtonFactory.create( 172 any(Context.class), 173 any(CallsManager.class))) 174 .thenReturn(mHeadsetMediaButton); 175 when(proximitySensorManagerFactory.create( 176 any(Context.class), 177 any(CallsManager.class))) 178 .thenReturn(mProximitySensorManager); 179 when(inCallWakeLockControllerFactory.create( 180 any(Context.class), 181 any(CallsManager.class))) 182 .thenReturn(mInCallWakeLockController); 183 184 mTelecomSystem = new TelecomSystem( 185 mComponentContextFixture.getTestDouble(), 186 mMissedCallNotifier, 187 mCallerInfoAsyncQueryFactoryFixture.getTestDouble(), 188 headsetMediaButtonFactory, 189 proximitySensorManagerFactory, 190 inCallWakeLockControllerFactory); 191 192 verify(headsetMediaButtonFactory).create( 193 eq(mComponentContextFixture.getTestDouble().getApplicationContext()), 194 any(CallsManager.class)); 195 verify(proximitySensorManagerFactory).create( 196 eq(mComponentContextFixture.getTestDouble().getApplicationContext()), 197 any(CallsManager.class)); 198 verify(inCallWakeLockControllerFactory).create( 199 eq(mComponentContextFixture.getTestDouble().getApplicationContext()), 200 any(CallsManager.class)); 201 } 202 203 private void setupConnectionServices() throws Exception { 204 mConnectionServiceFixtureA = new ConnectionServiceFixture(); 205 mConnectionServiceFixtureB = new ConnectionServiceFixture(); 206 207 mComponentContextFixture.addConnectionService( 208 mConnectionServiceComponentNameA, 209 mConnectionServiceFixtureA.getTestDouble()); 210 mComponentContextFixture.addConnectionService( 211 mConnectionServiceComponentNameB, 212 mConnectionServiceFixtureB.getTestDouble()); 213 214 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0); 215 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1); 216 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0); 217 } 218 219 private void setupInCallServices() throws Exception { 220 mComponentContextFixture.putResource( 221 com.android.server.telecom.R.string.ui_default_package, 222 mInCallServiceComponentNameX.getPackageName()); 223 mComponentContextFixture.putResource( 224 com.android.server.telecom.R.string.incall_default_class, 225 mInCallServiceComponentNameX.getClassName()); 226 227 mInCallServiceFixtureX = new InCallServiceFixture(); 228 mInCallServiceFixtureY = new InCallServiceFixture(); 229 230 mComponentContextFixture.addInCallService( 231 mInCallServiceComponentNameX, 232 mInCallServiceFixtureX.getTestDouble()); 233 mComponentContextFixture.addInCallService( 234 mInCallServiceComponentNameY, 235 mInCallServiceFixtureY.getTestDouble()); 236 } 237 238 private String startOutgoingPhoneCall( 239 String number, 240 PhoneAccountHandle phoneAccountHandle, 241 ConnectionServiceFixture connectionServiceFixture) throws Exception { 242 Intent actionCallIntent = new Intent(); 243 actionCallIntent.setData(Uri.parse("tel:" + number)); 244 actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); 245 actionCallIntent.setAction(Intent.ACTION_CALL); 246 if (phoneAccountHandle != null) { 247 actionCallIntent.putExtra( 248 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, 249 phoneAccountHandle); 250 } 251 252 mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent); 253 254 ArgumentCaptor<Intent> newOutgoingCallIntent = 255 ArgumentCaptor.forClass(Intent.class); 256 ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver = 257 ArgumentCaptor.forClass(BroadcastReceiver.class); 258 259 verify(mComponentContextFixture.getTestDouble().getApplicationContext()) 260 .sendOrderedBroadcastAsUser( 261 newOutgoingCallIntent.capture(), 262 any(UserHandle.class), 263 anyString(), 264 newOutgoingCallReceiver.capture(), 265 any(Handler.class), 266 anyInt(), 267 anyString(), 268 any(Bundle.class)); 269 270 assertNotNull(mInCallServiceFixtureX.mInCallAdapter); 271 assertNotNull(mInCallServiceFixtureY.mInCallAdapter); 272 273 // Pass on the new outgoing call Intent 274 // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive() 275 newOutgoingCallReceiver.getValue().setPendingResult( 276 new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0)); 277 newOutgoingCallReceiver.getValue().setResultData( 278 newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER)); 279 newOutgoingCallReceiver.getValue().onReceive( 280 mComponentContextFixture.getTestDouble(), 281 newOutgoingCallIntent.getValue()); 282 283 verify(connectionServiceFixture.getTestDouble()).createConnection( 284 eq(phoneAccountHandle), 285 anyString(), 286 any(ConnectionRequest.class), 287 anyBoolean(), 288 anyBoolean()); 289 290 String id = connectionServiceFixture.mLatestConnectionId; 291 292 connectionServiceFixture.sendHandleCreateConnectionComplete(id); 293 294 return id; 295 } 296 297 private String startIncomingPhoneCall( 298 String number, 299 PhoneAccountHandle phoneAccountHandle, 300 ConnectionServiceFixture connectionServiceFixture) throws Exception { 301 Bundle extras = new Bundle(); 302 extras.putParcelable( 303 TelephonyManager.EXTRA_INCOMING_NUMBER, 304 Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null)); 305 mTelecomSystem.getTelecomServiceImpl().getBinder() 306 .addNewIncomingCall(phoneAccountHandle, extras); 307 308 verify(connectionServiceFixture.getTestDouble()).createConnection( 309 any(PhoneAccountHandle.class), 310 anyString(), 311 any(ConnectionRequest.class), 312 eq(true), 313 eq(false)); 314 315 String id = connectionServiceFixture.mLatestConnectionId; 316 317 connectionServiceFixture.sendHandleCreateConnectionComplete(id); 318 connectionServiceFixture.sendSetRinging(id); 319 320 // For the case of incoming calls, Telecom connecting the InCall services and adding the 321 // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call 322 // is added, future interactions as triggered by the ConnectionService, through the various 323 // test fixtures, will be synchronous. 324 325 verify( 326 mInCallServiceFixtureX.getTestDouble(), 327 timeout(TEST_TIMEOUT)) 328 .setInCallAdapter( 329 any(IInCallAdapter.class)); 330 verify( 331 mInCallServiceFixtureY.getTestDouble(), 332 timeout(TEST_TIMEOUT)) 333 .setInCallAdapter( 334 any(IInCallAdapter.class)); 335 336 assertNotNull(mInCallServiceFixtureX.mInCallAdapter); 337 assertNotNull(mInCallServiceFixtureY.mInCallAdapter); 338 339 verify( 340 mInCallServiceFixtureX.getTestDouble(), 341 timeout(TEST_TIMEOUT)) 342 .addCall( 343 any(ParcelableCall.class)); 344 verify( 345 mInCallServiceFixtureY.getTestDouble(), 346 timeout(TEST_TIMEOUT)) 347 .addCall( 348 any(ParcelableCall.class)); 349 350 return id; 351 } 352 353 private void rapidFire(Runnable... tasks) { 354 final CyclicBarrier barrier = new CyclicBarrier(tasks.length); 355 final CountDownLatch latch = new CountDownLatch(tasks.length); 356 for (int i = 0; i < tasks.length; i++) { 357 final Runnable task = tasks[i]; 358 new Thread(new Runnable() { 359 @Override 360 public void run() { 361 try { 362 barrier.await(); 363 task.run(); 364 } catch (InterruptedException | BrokenBarrierException e){ 365 Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted"); 366 } finally { 367 latch.countDown(); 368 } 369 } 370 }).start(); 371 } 372 try { 373 latch.await(); 374 } catch (InterruptedException e) { 375 Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted"); 376 } 377 } 378 379 // A simple outgoing call, verifying that the appropriate connection service is contacted, 380 // the proper lifecycle is followed, and both In-Call Services are updated correctly. 381 public void testSingleOutgoingCall() throws Exception { 382 String connectionId = startOutgoingPhoneCall( 383 "650-555-1212", 384 mPhoneAccountA0.getAccountHandle(), 385 mConnectionServiceFixtureA); 386 387 assertEquals(1, mConnectionServiceFixtureA.mConnectionServiceAdapters.size()); 388 assertEquals(1, mConnectionServiceFixtureA.mConnectionById.size()); 389 390 mConnectionServiceFixtureA.sendSetDialing(connectionId); 391 392 assertEquals(1, mInCallServiceFixtureX.mCallById.size()); 393 String callId = mInCallServiceFixtureX.mLatestCallId; 394 395 assertEquals(CallState.DIALING, mInCallServiceFixtureX.getCall(callId).getState()); 396 assertEquals(CallState.DIALING, mInCallServiceFixtureY.getCall(callId).getState()); 397 398 mConnectionServiceFixtureA.sendSetActive(connectionId); 399 assertEquals(CallState.ACTIVE, mInCallServiceFixtureX.getCall(callId).getState()); 400 assertEquals(CallState.ACTIVE, mInCallServiceFixtureY.getCall(callId).getState()); 401 402 mInCallServiceFixtureX.mInCallAdapter.disconnectCall(callId);; 403 assertEquals(CallState.ACTIVE, mInCallServiceFixtureX.getCall(callId).getState()); 404 assertEquals(CallState.ACTIVE, mInCallServiceFixtureY.getCall(callId).getState()); 405 406 mConnectionServiceFixtureA.sendSetDisconnected(connectionId, DisconnectCause.LOCAL); 407 assertEquals(CallState.DISCONNECTED, mInCallServiceFixtureX.getCall(callId).getState()); 408 assertEquals(CallState.DISCONNECTED, mInCallServiceFixtureY.getCall(callId).getState()); 409 } 410 411 // A simple incoming call, similar in scope to the previous test 412 public void testSingleIncomingCall() throws Exception { 413 String connectionId = startIncomingPhoneCall( 414 "650-555-1212", 415 mPhoneAccountA0.getAccountHandle(), 416 mConnectionServiceFixtureA); 417 418 assertEquals(1, mConnectionServiceFixtureA.mConnectionServiceAdapters.size()); 419 assertEquals(1, mConnectionServiceFixtureA.mConnectionById.size()); 420 421 assertEquals(1, mInCallServiceFixtureX.mCallById.size()); 422 String callId = mInCallServiceFixtureX.mLatestCallId; 423 424 assertEquals(CallState.RINGING, mInCallServiceFixtureX.getCall(callId).getState()); 425 assertEquals(CallState.RINGING, mInCallServiceFixtureY.getCall(callId).getState()); 426 427 mConnectionServiceFixtureA.sendSetActive(connectionId); 428 assertEquals(CallState.ACTIVE, mInCallServiceFixtureX.getCall(callId).getState()); 429 assertEquals(CallState.ACTIVE, mInCallServiceFixtureY.getCall(callId).getState()); 430 431 mInCallServiceFixtureX.mInCallAdapter.disconnectCall(callId);; 432 assertEquals(CallState.ACTIVE, mInCallServiceFixtureX.getCall(callId).getState()); 433 assertEquals(CallState.ACTIVE, mInCallServiceFixtureY.getCall(callId).getState()); 434 435 mConnectionServiceFixtureA.sendSetDisconnected(connectionId, DisconnectCause.LOCAL); 436 assertEquals(CallState.DISCONNECTED, mInCallServiceFixtureX.getCall(callId).getState()); 437 assertEquals(CallState.DISCONNECTED, mInCallServiceFixtureY.getCall(callId).getState()); 438 } 439 440 public void testDeadlockOnOutgoingCall() throws Exception { 441 for (int i = 0; i < 100; i++) { 442 TelecomSystemTest test = new TelecomSystemTest(); 443 test.setContext(getContext()); 444 test.setTestContext(getTestContext()); 445 test.setName(getName()); 446 test.setUp(); 447 test.do_testDeadlockOnOutgoingCall(); 448 test.tearDown(); 449 } 450 } 451 452 public void do_testDeadlockOnOutgoingCall() throws Exception { 453 final String connectionId = startOutgoingPhoneCall( 454 "650-555-1212", 455 mPhoneAccountA0.getAccountHandle(), 456 mConnectionServiceFixtureA); 457 rapidFire( 458 new Runnable() { 459 @Override 460 public void run() { 461 while (mCallerInfoAsyncQueryFactoryFixture.mRequests.size() > 0) { 462 mCallerInfoAsyncQueryFactoryFixture.mRequests.remove(0).reply(); 463 } 464 } 465 }, 466 new Runnable() { 467 @Override 468 public void run() { 469 try { 470 mConnectionServiceFixtureA.sendSetActive(connectionId); 471 } catch (Exception e) { 472 Log.e(this, e, ""); 473 } 474 } 475 }); 476 } 477} 478