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