Home | History | Annotate | Download | only in tests
      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 
     17 package com.android.server.telecom.tests;
     18 
     19 import com.google.common.base.Predicate;
     20 
     21 import static org.mockito.Matchers.any;
     22 import static org.mockito.Matchers.anyBoolean;
     23 import static org.mockito.Matchers.anyInt;
     24 import static org.mockito.Matchers.anyString;
     25 import static org.mockito.Matchers.eq;
     26 import static org.mockito.Mockito.mock;
     27 import static org.mockito.Mockito.reset;
     28 import static org.mockito.Mockito.timeout;
     29 import static org.mockito.Mockito.verify;
     30 import static org.mockito.Mockito.when;
     31 
     32 import android.content.BroadcastReceiver;
     33 import android.content.ComponentName;
     34 import android.content.Context;
     35 import android.content.Intent;
     36 import android.media.AudioManager;
     37 import android.net.Uri;
     38 import android.os.Bundle;
     39 import android.os.Handler;
     40 import android.os.UserHandle;
     41 import android.telecom.Call;
     42 import android.telecom.CallAudioState;
     43 import android.telecom.Connection;
     44 import android.telecom.ConnectionRequest;
     45 import android.telecom.DisconnectCause;
     46 import android.telecom.ParcelableCall;
     47 import android.telecom.PhoneAccount;
     48 import android.telecom.PhoneAccountHandle;
     49 import android.telecom.TelecomManager;
     50 import android.telecom.VideoProfile;
     51 import android.telephony.TelephonyManager;
     52 
     53 import com.android.internal.telecom.IInCallAdapter;
     54 import com.android.server.telecom.CallsManager;
     55 import com.android.server.telecom.HeadsetMediaButton;
     56 import com.android.server.telecom.HeadsetMediaButtonFactory;
     57 import com.android.server.telecom.InCallWakeLockController;
     58 import com.android.server.telecom.InCallWakeLockControllerFactory;
     59 import com.android.server.telecom.Log;
     60 import com.android.server.telecom.MissedCallNotifier;
     61 import com.android.server.telecom.ProximitySensorManager;
     62 import com.android.server.telecom.ProximitySensorManagerFactory;
     63 import com.android.server.telecom.TelecomSystem;
     64 
     65 import org.mockito.ArgumentCaptor;
     66 import org.mockito.Mock;
     67 
     68 import java.util.concurrent.BrokenBarrierException;
     69 import java.util.concurrent.CountDownLatch;
     70 import java.util.concurrent.CyclicBarrier;
     71 
     72 public class TelecomSystemTest extends TelecomTestCase {
     73 
     74     static final int TEST_POLL_INTERVAL = 10;  // milliseconds
     75     static final int TEST_TIMEOUT = 1000;  // milliseconds
     76 
     77     @Mock MissedCallNotifier mMissedCallNotifier;
     78     @Mock HeadsetMediaButton mHeadsetMediaButton;
     79     @Mock ProximitySensorManager mProximitySensorManager;
     80     @Mock InCallWakeLockController mInCallWakeLockController;
     81 
     82     final ComponentName mInCallServiceComponentNameX =
     83             new ComponentName(
     84                     "incall-service-package-X",
     85                     "incall-service-class-X");
     86     final ComponentName mInCallServiceComponentNameY =
     87             new ComponentName(
     88                     "incall-service-package-Y",
     89                     "incall-service-class-Y");
     90 
     91     InCallServiceFixture mInCallServiceFixtureX;
     92     InCallServiceFixture mInCallServiceFixtureY;
     93 
     94     final ComponentName mConnectionServiceComponentNameA =
     95             new ComponentName(
     96                     "connection-service-package-A",
     97                     "connection-service-class-A");
     98     final ComponentName mConnectionServiceComponentNameB =
     99             new ComponentName(
    100                     "connection-service-package-B",
    101                     "connection-service-class-B");
    102 
    103     final PhoneAccount mPhoneAccountA0 =
    104             PhoneAccount.builder(
    105                     new PhoneAccountHandle(
    106                             mConnectionServiceComponentNameA,
    107                             "id A 0"),
    108                     "Phone account service A ID 0")
    109                     .addSupportedUriScheme("tel")
    110                     .setCapabilities(
    111                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
    112                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
    113                     .build();
    114     final PhoneAccount mPhoneAccountA1 =
    115             PhoneAccount.builder(
    116                     new PhoneAccountHandle(
    117                             mConnectionServiceComponentNameA,
    118                             "id A 1"),
    119                     "Phone account service A ID 1")
    120                     .addSupportedUriScheme("tel")
    121                     .setCapabilities(
    122                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
    123                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
    124                     .build();
    125     final PhoneAccount mPhoneAccountB0 =
    126             PhoneAccount.builder(
    127                     new PhoneAccountHandle(
    128                             mConnectionServiceComponentNameB,
    129                             "id B 0"),
    130                     "Phone account service B ID 0")
    131                     .addSupportedUriScheme("tel")
    132                     .setCapabilities(
    133                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
    134                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
    135                     .build();
    136 
    137     ConnectionServiceFixture mConnectionServiceFixtureA;
    138     ConnectionServiceFixture mConnectionServiceFixtureB;
    139 
    140     CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
    141 
    142     TelecomSystem mTelecomSystem;
    143 
    144     class IdPair {
    145         final String mConnectionId;
    146         final String mCallId;
    147 
    148         public IdPair(String connectionId, String callId) {
    149             this.mConnectionId = connectionId;
    150             this.mCallId = callId;
    151         }
    152     }
    153 
    154     @Override
    155     public void setUp() throws Exception {
    156         super.setUp();
    157 
    158         // First set up information about the In-Call services in the mock Context, since
    159         // Telecom will search for these as soon as it is instantiated
    160         setupInCallServices();
    161 
    162         // Next, create the TelecomSystem, our system under test
    163         setupTelecomSystem();
    164 
    165         // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
    166         // now-running TelecomSystem
    167         setupConnectionServices();
    168     }
    169 
    170     @Override
    171     public void tearDown() throws Exception {
    172         mTelecomSystem = null;
    173         super.tearDown();
    174     }
    175 
    176     private void setupTelecomSystem() throws Exception {
    177         HeadsetMediaButtonFactory headsetMediaButtonFactory =
    178                 mock(HeadsetMediaButtonFactory.class);
    179         ProximitySensorManagerFactory proximitySensorManagerFactory =
    180                 mock(ProximitySensorManagerFactory.class);
    181         InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
    182                 mock(InCallWakeLockControllerFactory.class);
    183 
    184         mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
    185 
    186         when(headsetMediaButtonFactory.create(
    187                 any(Context.class),
    188                 any(CallsManager.class),
    189                 any(TelecomSystem.SyncRoot.class)))
    190                 .thenReturn(mHeadsetMediaButton);
    191         when(proximitySensorManagerFactory.create(
    192                 any(Context.class),
    193                 any(CallsManager.class)))
    194                 .thenReturn(mProximitySensorManager);
    195         when(inCallWakeLockControllerFactory.create(
    196                 any(Context.class),
    197                 any(CallsManager.class)))
    198                 .thenReturn(mInCallWakeLockController);
    199 
    200         mTelecomSystem = new TelecomSystem(
    201                 mComponentContextFixture.getTestDouble(),
    202                 mMissedCallNotifier,
    203                 mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
    204                 headsetMediaButtonFactory,
    205                 proximitySensorManagerFactory,
    206                 inCallWakeLockControllerFactory);
    207 
    208         verify(headsetMediaButtonFactory).create(
    209                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
    210                 any(CallsManager.class),
    211                 any(TelecomSystem.SyncRoot.class));
    212         verify(proximitySensorManagerFactory).create(
    213                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
    214                 any(CallsManager.class));
    215         verify(inCallWakeLockControllerFactory).create(
    216                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
    217                 any(CallsManager.class));
    218     }
    219 
    220     private void setupConnectionServices() throws Exception {
    221         mConnectionServiceFixtureA = new ConnectionServiceFixture();
    222         mConnectionServiceFixtureB = new ConnectionServiceFixture();
    223 
    224         mComponentContextFixture.addConnectionService(
    225                 mConnectionServiceComponentNameA,
    226                 mConnectionServiceFixtureA.getTestDouble());
    227         mComponentContextFixture.addConnectionService(
    228                 mConnectionServiceComponentNameB,
    229                 mConnectionServiceFixtureB.getTestDouble());
    230 
    231         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
    232         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
    233         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
    234 
    235         mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
    236                 mPhoneAccountA0.getAccountHandle());
    237     }
    238 
    239     private void setupInCallServices() throws Exception {
    240         mComponentContextFixture.putResource(
    241                 com.android.server.telecom.R.string.ui_default_package,
    242                 mInCallServiceComponentNameX.getPackageName());
    243         mComponentContextFixture.putResource(
    244                 com.android.server.telecom.R.string.incall_default_class,
    245                 mInCallServiceComponentNameX.getClassName());
    246 
    247         mInCallServiceFixtureX = new InCallServiceFixture();
    248         mInCallServiceFixtureY = new InCallServiceFixture();
    249 
    250         mComponentContextFixture.addInCallService(
    251                 mInCallServiceComponentNameX,
    252                 mInCallServiceFixtureX.getTestDouble());
    253         mComponentContextFixture.addInCallService(
    254                 mInCallServiceComponentNameY,
    255                 mInCallServiceFixtureY.getTestDouble());
    256     }
    257 
    258     private IdPair startOutgoingPhoneCall(
    259             String number,
    260             PhoneAccountHandle phoneAccountHandle,
    261             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    262         reset(
    263                 connectionServiceFixture.getTestDouble(),
    264                 mInCallServiceFixtureX.getTestDouble(),
    265                 mInCallServiceFixtureY.getTestDouble());
    266 
    267         assertEquals(
    268                 mInCallServiceFixtureX.mCallById.size(),
    269                 mInCallServiceFixtureY.mCallById.size());
    270         assertEquals(
    271                 (mInCallServiceFixtureX.mInCallAdapter != null),
    272                 (mInCallServiceFixtureY.mInCallAdapter != null));
    273 
    274         int startingNumConnections = connectionServiceFixture.mConnectionById.size();
    275         int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
    276         boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
    277 
    278         Intent actionCallIntent = new Intent();
    279         actionCallIntent.setData(Uri.parse("tel:" + number));
    280         actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
    281         actionCallIntent.setAction(Intent.ACTION_CALL);
    282         if (phoneAccountHandle != null) {
    283             actionCallIntent.putExtra(
    284                     TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
    285                     phoneAccountHandle);
    286         }
    287 
    288         mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent);
    289 
    290         if (!hasInCallAdapter) {
    291             verify(mInCallServiceFixtureX.getTestDouble())
    292                     .setInCallAdapter(
    293                             any(IInCallAdapter.class));
    294             verify(mInCallServiceFixtureY.getTestDouble())
    295                     .setInCallAdapter(
    296                             any(IInCallAdapter.class));
    297         }
    298 
    299         ArgumentCaptor<Intent> newOutgoingCallIntent =
    300                 ArgumentCaptor.forClass(Intent.class);
    301         ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
    302                 ArgumentCaptor.forClass(BroadcastReceiver.class);
    303 
    304         verify(mComponentContextFixture.getTestDouble().getApplicationContext())
    305                 .sendOrderedBroadcastAsUser(
    306                         newOutgoingCallIntent.capture(),
    307                         any(UserHandle.class),
    308                         anyString(),
    309                         anyInt(),
    310                         newOutgoingCallReceiver.capture(),
    311                         any(Handler.class),
    312                         anyInt(),
    313                         anyString(),
    314                         any(Bundle.class));
    315 
    316         // Pass on the new outgoing call Intent
    317         // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
    318         newOutgoingCallReceiver.getValue().setPendingResult(
    319                 new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
    320         newOutgoingCallReceiver.getValue().setResultData(
    321                 newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
    322         newOutgoingCallReceiver.getValue().onReceive(
    323                 mComponentContextFixture.getTestDouble(),
    324                 newOutgoingCallIntent.getValue());
    325 
    326         assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
    327 
    328         verify(connectionServiceFixture.getTestDouble()).createConnection(
    329                 eq(phoneAccountHandle),
    330                 anyString(),
    331                 any(ConnectionRequest.class),
    332                 anyBoolean(),
    333                 anyBoolean());
    334 
    335         connectionServiceFixture.sendHandleCreateConnectionComplete(
    336                 connectionServiceFixture.mLatestConnectionId);
    337 
    338         assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
    339         assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
    340 
    341         assertEquals(
    342                 mInCallServiceFixtureX.mLatestCallId,
    343                 mInCallServiceFixtureY.mLatestCallId);
    344 
    345         return new IdPair(
    346                 connectionServiceFixture.mLatestConnectionId,
    347                 mInCallServiceFixtureX.mLatestCallId);
    348     }
    349 
    350     private IdPair startIncomingPhoneCall(
    351             String number,
    352             PhoneAccountHandle phoneAccountHandle,
    353             final ConnectionServiceFixture connectionServiceFixture) throws Exception {
    354         reset(
    355                 connectionServiceFixture.getTestDouble(),
    356                 mInCallServiceFixtureX.getTestDouble(),
    357                 mInCallServiceFixtureY.getTestDouble());
    358 
    359         assertEquals(
    360                 mInCallServiceFixtureX.mCallById.size(),
    361                 mInCallServiceFixtureY.mCallById.size());
    362         assertEquals(
    363                 (mInCallServiceFixtureX.mInCallAdapter != null),
    364                 (mInCallServiceFixtureY.mInCallAdapter != null));
    365 
    366         final int startingNumConnections = connectionServiceFixture.mConnectionById.size();
    367         final int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
    368         boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
    369 
    370         Bundle extras = new Bundle();
    371         extras.putParcelable(
    372                 TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
    373                 Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
    374         mTelecomSystem.getTelecomServiceImpl().getBinder()
    375                 .addNewIncomingCall(phoneAccountHandle, extras);
    376 
    377         verify(connectionServiceFixture.getTestDouble()).createConnection(
    378                 any(PhoneAccountHandle.class),
    379                 anyString(),
    380                 any(ConnectionRequest.class),
    381                 eq(true),
    382                 eq(false));
    383 
    384         connectionServiceFixture.sendHandleCreateConnectionComplete(
    385                 connectionServiceFixture.mLatestConnectionId);
    386         connectionServiceFixture.sendSetRinging(
    387                 connectionServiceFixture.mLatestConnectionId);
    388 
    389         // For the case of incoming calls, Telecom connecting the InCall services and adding the
    390         // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
    391         // is added, future interactions as triggered by the ConnectionService, through the various
    392         // test fixtures, will be synchronous.
    393 
    394         if (!hasInCallAdapter) {
    395             verify(
    396                     mInCallServiceFixtureX.getTestDouble(),
    397                     timeout(TEST_TIMEOUT))
    398                     .setInCallAdapter(
    399                             any(IInCallAdapter.class));
    400             verify(
    401                     mInCallServiceFixtureY.getTestDouble(),
    402                     timeout(TEST_TIMEOUT))
    403                     .setInCallAdapter(
    404                             any(IInCallAdapter.class));
    405         }
    406 
    407         // Give the InCallService time to respond
    408 
    409         assertTrueWithTimeout(new Predicate<Void>() {
    410             @Override
    411             public boolean apply(Void v) {
    412                 return mInCallServiceFixtureX.mInCallAdapter != null;
    413             }
    414         });
    415 
    416         assertTrueWithTimeout(new Predicate<Void>() {
    417             @Override
    418             public boolean apply(Void v) {
    419                 return mInCallServiceFixtureY.mInCallAdapter != null;
    420             }
    421         });
    422 
    423         verify(
    424                 mInCallServiceFixtureX.getTestDouble(),
    425                 timeout(TEST_TIMEOUT))
    426                 .addCall(
    427                         any(ParcelableCall.class));
    428         verify(
    429                 mInCallServiceFixtureY.getTestDouble(),
    430                 timeout(TEST_TIMEOUT))
    431                 .addCall(
    432                         any(ParcelableCall.class));
    433 
    434         // Give the InCallService time to respond
    435 
    436         assertTrueWithTimeout(new Predicate<Void>() {
    437             @Override
    438             public boolean apply(Void v) {
    439                 return startingNumConnections + 1 ==
    440                         connectionServiceFixture.mConnectionById.size();
    441             }
    442         });
    443         assertTrueWithTimeout(new Predicate<Void>() {
    444             @Override
    445             public boolean apply(Void v) {
    446                 return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
    447             }
    448         });
    449         assertTrueWithTimeout(new Predicate<Void>() {
    450             @Override
    451             public boolean apply(Void v) {
    452                 return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
    453             }
    454         });
    455 
    456         assertEquals(
    457                 mInCallServiceFixtureX.mLatestCallId,
    458                 mInCallServiceFixtureY.mLatestCallId);
    459 
    460         return new IdPair(
    461                 connectionServiceFixture.mLatestConnectionId,
    462                 mInCallServiceFixtureX.mLatestCallId);
    463     }
    464 
    465     private void rapidFire(Runnable... tasks) {
    466         final CyclicBarrier barrier = new CyclicBarrier(tasks.length);
    467         final CountDownLatch latch = new CountDownLatch(tasks.length);
    468         for (int i = 0; i < tasks.length; i++) {
    469             final Runnable task = tasks[i];
    470             new Thread(new Runnable() {
    471                 @Override
    472                 public void run() {
    473                     try {
    474                         barrier.await();
    475                         task.run();
    476                     } catch (InterruptedException | BrokenBarrierException e){
    477                         Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
    478                     } finally {
    479                         latch.countDown();
    480                     }
    481                 }
    482             }).start();
    483         }
    484         try {
    485             latch.await();
    486         } catch (InterruptedException e) {
    487             Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
    488         }
    489     }
    490 
    491     // A simple outgoing call, verifying that the appropriate connection service is contacted,
    492     // the proper lifecycle is followed, and both In-Call Services are updated correctly.
    493     private IdPair startAndMakeActiveOutgoingCall(
    494             String number,
    495             PhoneAccountHandle phoneAccountHandle,
    496             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    497         IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
    498 
    499         connectionServiceFixture.sendSetDialing(ids.mConnectionId);
    500         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    501         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    502 
    503         connectionServiceFixture.sendSetActive(ids.mConnectionId);
    504         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    505         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    506 
    507         return ids;
    508     }
    509 
    510     public void testSingleOutgoingCallLocalDisconnect() throws Exception {
    511         IdPair ids = startAndMakeActiveOutgoingCall(
    512                 "650-555-1212",
    513                 mPhoneAccountA0.getAccountHandle(),
    514                 mConnectionServiceFixtureA);
    515 
    516         mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
    517         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    518         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    519 
    520         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
    521         assertEquals(Call.STATE_DISCONNECTED,
    522                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    523         assertEquals(Call.STATE_DISCONNECTED,
    524                 mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    525     }
    526 
    527     public void testSingleOutgoingCallRemoteDisconnect() throws Exception {
    528         IdPair ids = startAndMakeActiveOutgoingCall(
    529                 "650-555-1212",
    530                 mPhoneAccountA0.getAccountHandle(),
    531                 mConnectionServiceFixtureA);
    532 
    533         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
    534         assertEquals(Call.STATE_DISCONNECTED,
    535                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    536         assertEquals(Call.STATE_DISCONNECTED,
    537                 mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    538     }
    539 
    540     // A simple incoming call, similar in scope to the previous test
    541     private IdPair startAndMakeActiveIncomingCall(
    542             String number,
    543             PhoneAccountHandle phoneAccountHandle,
    544             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    545         IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
    546 
    547         assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    548         assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    549 
    550         mInCallServiceFixtureX.mInCallAdapter
    551                 .answerCall(ids.mCallId, VideoProfile.STATE_AUDIO_ONLY);
    552 
    553         verify(connectionServiceFixture.getTestDouble())
    554                 .answer(ids.mConnectionId);
    555 
    556         connectionServiceFixture.sendSetActive(ids.mConnectionId);
    557         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    558         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    559 
    560         return ids;
    561     }
    562 
    563     public void testSingleIncomingCallLocalDisconnect() throws Exception {
    564         IdPair ids = startAndMakeActiveIncomingCall(
    565                 "650-555-1212",
    566                 mPhoneAccountA0.getAccountHandle(),
    567                 mConnectionServiceFixtureA);
    568 
    569         mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
    570         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    571         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    572 
    573         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
    574         assertEquals(Call.STATE_DISCONNECTED,
    575                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    576         assertEquals(Call.STATE_DISCONNECTED,
    577                 mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    578     }
    579 
    580     public void testSingleIncomingCallRemoteDisconnect() throws Exception {
    581         IdPair ids = startAndMakeActiveIncomingCall(
    582                 "650-555-1212",
    583                 mPhoneAccountA0.getAccountHandle(),
    584                 mConnectionServiceFixtureA);
    585 
    586         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
    587         assertEquals(Call.STATE_DISCONNECTED,
    588                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    589         assertEquals(Call.STATE_DISCONNECTED,
    590                 mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    591     }
    592 
    593     public void do_testDeadlockOnOutgoingCall() throws Exception {
    594         final IdPair ids = startOutgoingPhoneCall(
    595                 "650-555-1212",
    596                 mPhoneAccountA0.getAccountHandle(),
    597                 mConnectionServiceFixtureA);
    598         rapidFire(
    599                 new Runnable() {
    600                     @Override
    601                     public void run() {
    602                         while (mCallerInfoAsyncQueryFactoryFixture.mRequests.size() > 0) {
    603                             mCallerInfoAsyncQueryFactoryFixture.mRequests.remove(0).reply();
    604                         }
    605                     }
    606                 },
    607                 new Runnable() {
    608                     @Override
    609                     public void run() {
    610                         try {
    611                             mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
    612                         } catch (Exception e) {
    613                             Log.e(this, e, "");
    614                         }
    615                     }
    616                 });
    617     }
    618 
    619     public void testDeadlockOnOutgoingCall() throws Exception {
    620         for (int i = 0; i < 100; i++) {
    621             TelecomSystemTest test = new TelecomSystemTest();
    622             test.setContext(getContext());
    623             test.setTestContext(getTestContext());
    624             test.setName(getName());
    625             test.setUp();
    626             test.do_testDeadlockOnOutgoingCall();
    627             test.tearDown();
    628         }
    629     }
    630 
    631     public void testIncomingThenOutgoingCalls() throws Exception {
    632         // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
    633         IdPair incoming = startAndMakeActiveIncomingCall(
    634                 "650-555-2323",
    635                 mPhoneAccountA0.getAccountHandle(),
    636                 mConnectionServiceFixtureA);
    637         IdPair outgoing = startAndMakeActiveOutgoingCall(
    638                 "650-555-1212",
    639                 mPhoneAccountA0.getAccountHandle(),
    640                 mConnectionServiceFixtureA);
    641     }
    642 
    643     public void testOutgoingThenIncomingCalls() throws Exception {
    644         // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
    645         IdPair outgoing = startAndMakeActiveOutgoingCall(
    646                 "650-555-1212",
    647                 mPhoneAccountA0.getAccountHandle(),
    648                 mConnectionServiceFixtureA);
    649         IdPair incoming = startAndMakeActiveIncomingCall(
    650                 "650-555-2323",
    651                 mPhoneAccountA0.getAccountHandle(),
    652                 mConnectionServiceFixtureA);
    653         verify(mConnectionServiceFixtureA.getTestDouble())
    654                 .hold(outgoing.mConnectionId);
    655         mConnectionServiceFixtureA.mConnectionById.get(outgoing.mConnectionId).state =
    656                 Connection.STATE_HOLDING;
    657         mConnectionServiceFixtureA.sendSetOnHold(outgoing.mConnectionId);
    658         assertEquals(
    659                 Call.STATE_HOLDING,
    660                 mInCallServiceFixtureX.getCall(outgoing.mCallId).getState());
    661         assertEquals(
    662                 Call.STATE_HOLDING,
    663                 mInCallServiceFixtureY.getCall(outgoing.mCallId).getState());
    664     }
    665 
    666     public void testAudioManagerOperations() throws Exception {
    667         AudioManager audioManager = (AudioManager) mComponentContextFixture.getTestDouble()
    668                 .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
    669 
    670         IdPair outgoing = startAndMakeActiveOutgoingCall(
    671                 "650-555-1212",
    672                 mPhoneAccountA0.getAccountHandle(),
    673                 mConnectionServiceFixtureA);
    674 
    675         verify(audioManager, timeout(TEST_TIMEOUT))
    676                 .requestAudioFocusForCall(anyInt(), anyInt());
    677         verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce())
    678                 .setMode(AudioManager.MODE_IN_CALL);
    679 
    680         mInCallServiceFixtureX.mInCallAdapter.mute(true);
    681         verify(audioManager, timeout(TEST_TIMEOUT))
    682                 .setMicrophoneMute(true);
    683         mInCallServiceFixtureX.mInCallAdapter.mute(false);
    684         verify(audioManager, timeout(TEST_TIMEOUT))
    685                 .setMicrophoneMute(false);
    686 
    687         mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
    688         verify(audioManager, timeout(TEST_TIMEOUT))
    689                 .setSpeakerphoneOn(true);
    690         mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
    691         verify(audioManager, timeout(TEST_TIMEOUT))
    692                 .setSpeakerphoneOn(false);
    693 
    694         mConnectionServiceFixtureA.
    695                 sendSetDisconnected(outgoing.mConnectionId, DisconnectCause.REMOTE);
    696 
    697         verify(audioManager, timeout(TEST_TIMEOUT))
    698                 .abandonAudioFocusForCall();
    699         verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce())
    700                 .setMode(AudioManager.MODE_NORMAL);
    701     }
    702 
    703     protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
    704         int elapsed = 0;
    705         while (elapsed < TEST_TIMEOUT) {
    706             if (predicate.apply(null)) {
    707                 return;
    708             } else {
    709                 try {
    710                     Thread.sleep(TEST_POLL_INTERVAL);
    711                     elapsed += TEST_POLL_INTERVAL;
    712                 } catch (InterruptedException e) {
    713                     fail(e.toString());
    714                 }
    715             }
    716         }
    717         fail("Timeout in assertTrueWithTimeout");
    718     }
    719 }
    720