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 
20 import static org.mockito.Matchers.any;
21 import static org.mockito.Matchers.anyBoolean;
22 import static org.mockito.Matchers.anyInt;
23 import static org.mockito.Matchers.anyString;
24 import static org.mockito.Matchers.eq;
25 import static org.mockito.Matchers.isNull;
26 import static org.mockito.Mockito.doAnswer;
27 import static org.mockito.Mockito.doReturn;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.reset;
30 import static org.mockito.Mockito.spy;
31 import static org.mockito.Mockito.timeout;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
35 
36 import android.content.BroadcastReceiver;
37 import android.content.ComponentName;
38 import android.content.ContentResolver;
39 import android.content.Context;
40 import android.content.IContentProvider;
41 import android.content.Intent;
42 import android.media.AudioManager;
43 import android.media.IAudioService;
44 import android.net.Uri;
45 import android.os.Bundle;
46 import android.os.Handler;
47 import android.os.Process;
48 import android.os.UserHandle;
49 import android.provider.BlockedNumberContract;
50 import android.telecom.Call;
51 import android.telecom.ConnectionRequest;
52 import android.telecom.ParcelableCall;
53 import android.telecom.PhoneAccount;
54 import android.telecom.PhoneAccountHandle;
55 import android.telecom.TelecomManager;
56 import android.telecom.VideoProfile;
57 
58 import com.android.internal.telecom.IInCallAdapter;
59 import com.android.server.telecom.AsyncRingtonePlayer;
60 import com.android.server.telecom.BluetoothPhoneServiceImpl;
61 import com.android.server.telecom.CallAudioManager;
62 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
63 import com.android.server.telecom.CallsManager;
64 import com.android.server.telecom.CallsManagerListenerBase;
65 import com.android.server.telecom.ContactsAsyncHelper;
66 import com.android.server.telecom.HeadsetMediaButton;
67 import com.android.server.telecom.HeadsetMediaButtonFactory;
68 import com.android.server.telecom.InCallWakeLockController;
69 import com.android.server.telecom.InCallWakeLockControllerFactory;
70 import com.android.server.telecom.MissedCallNotifier;
71 import com.android.server.telecom.PhoneAccountRegistrar;
72 import com.android.server.telecom.ProximitySensorManager;
73 import com.android.server.telecom.ProximitySensorManagerFactory;
74 import com.android.server.telecom.TelecomSystem;
75 import com.android.server.telecom.Timeouts;
76 import com.android.server.telecom.components.UserCallIntentProcessor;
77 import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
78 
79 import com.google.common.base.Predicate;
80 
81 import org.mockito.ArgumentCaptor;
82 import org.mockito.Mock;
83 import org.mockito.invocation.InvocationOnMock;
84 import org.mockito.stubbing.Answer;
85 
86 import java.util.ArrayList;
87 import java.util.List;
88 
89 /**
90  * Implements mocks and functionality required to implement telecom system tests.
91  */
92 public class TelecomSystemTest extends TelecomTestCase {
93 
94     static final int TEST_POLL_INTERVAL = 10;  // milliseconds
95     static final int TEST_TIMEOUT = 1000;  // milliseconds
96 
97     public class HeadsetMediaButtonFactoryF implements HeadsetMediaButtonFactory  {
98         @Override
create(Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock)99         public HeadsetMediaButton create(Context context, CallsManager callsManager,
100                 TelecomSystem.SyncRoot lock) {
101             return mHeadsetMediaButton;
102         }
103     }
104 
105     public class ProximitySensorManagerFactoryF implements ProximitySensorManagerFactory {
106         @Override
create(Context context, CallsManager callsManager)107         public ProximitySensorManager create(Context context, CallsManager callsManager) {
108             return mProximitySensorManager;
109         }
110     }
111 
112     public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory {
113         @Override
create(Context context, CallsManager callsManager)114         public InCallWakeLockController create(Context context, CallsManager callsManager) {
115             return mInCallWakeLockController;
116         }
117     }
118 
119     public static class MissedCallNotifierFakeImpl extends CallsManagerListenerBase
120             implements MissedCallNotifier {
121         List<com.android.server.telecom.Call> missedCallsNotified = new ArrayList<>();
122 
123         @Override
clearMissedCalls(UserHandle userHandle)124         public void clearMissedCalls(UserHandle userHandle) {
125 
126         }
127 
128         @Override
showMissedCallNotification(com.android.server.telecom.Call call)129         public void showMissedCallNotification(com.android.server.telecom.Call call) {
130             missedCallsNotified.add(call);
131         }
132 
133         @Override
reloadFromDatabase(TelecomSystem.SyncRoot lock, CallsManager callsManager, ContactsAsyncHelper contactsAsyncHelper, CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, UserHandle userHandle)134         public void reloadFromDatabase(TelecomSystem.SyncRoot lock, CallsManager callsManager,
135                 ContactsAsyncHelper contactsAsyncHelper,
136                 CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, UserHandle userHandle) {
137 
138         }
139 
140         @Override
setCurrentUserHandle(UserHandle userHandle)141         public void setCurrentUserHandle(UserHandle userHandle) {
142 
143         }
144     }
145 
146     MissedCallNotifierFakeImpl mMissedCallNotifier = new MissedCallNotifierFakeImpl();
147     @Mock HeadsetMediaButton mHeadsetMediaButton;
148     @Mock ProximitySensorManager mProximitySensorManager;
149     @Mock InCallWakeLockController mInCallWakeLockController;
150     @Mock BluetoothPhoneServiceImpl mBluetoothPhoneServiceImpl;
151     @Mock AsyncRingtonePlayer mAsyncRingtonePlayer;
152 
153     final ComponentName mInCallServiceComponentNameX =
154             new ComponentName(
155                     "incall-service-package-X",
156                     "incall-service-class-X");
157     final ComponentName mInCallServiceComponentNameY =
158             new ComponentName(
159                     "incall-service-package-Y",
160                     "incall-service-class-Y");
161 
162     InCallServiceFixture mInCallServiceFixtureX;
163     InCallServiceFixture mInCallServiceFixtureY;
164 
165     final ComponentName mConnectionServiceComponentNameA =
166             new ComponentName(
167                     "connection-service-package-A",
168                     "connection-service-class-A");
169     final ComponentName mConnectionServiceComponentNameB =
170             new ComponentName(
171                     "connection-service-package-B",
172                     "connection-service-class-B");
173 
174     final PhoneAccount mPhoneAccountA0 =
175             PhoneAccount.builder(
176                     new PhoneAccountHandle(
177                             mConnectionServiceComponentNameA,
178                             "id A 0"),
179                     "Phone account service A ID 0")
180                     .addSupportedUriScheme("tel")
181                     .setCapabilities(
182                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
183                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
184                     .build();
185     final PhoneAccount mPhoneAccountA1 =
186             PhoneAccount.builder(
187                     new PhoneAccountHandle(
188                             mConnectionServiceComponentNameA,
189                             "id A 1"),
190                     "Phone account service A ID 1")
191                     .addSupportedUriScheme("tel")
192                     .setCapabilities(
193                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
194                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
195                     .build();
196     final PhoneAccount mPhoneAccountB0 =
197             PhoneAccount.builder(
198                     new PhoneAccountHandle(
199                             mConnectionServiceComponentNameB,
200                             "id B 0"),
201                     "Phone account service B ID 0")
202                     .addSupportedUriScheme("tel")
203                     .setCapabilities(
204                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
205                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
206                     .build();
207 
208     ConnectionServiceFixture mConnectionServiceFixtureA;
209     ConnectionServiceFixture mConnectionServiceFixtureB;
210     Timeouts.Adapter mTimeoutsAdapter;
211 
212     CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
213 
214     IAudioService mAudioService;
215 
216     TelecomSystem mTelecomSystem;
217 
218     Context mSpyContext;
219 
220     private int mNumOutgoingCallsMade;
221 
222     class IdPair {
223         final String mConnectionId;
224         final String mCallId;
225 
IdPair(String connectionId, String callId)226         public IdPair(String connectionId, String callId) {
227             this.mConnectionId = connectionId;
228             this.mCallId = callId;
229         }
230     }
231 
232     @Override
setUp()233     public void setUp() throws Exception {
234         super.setUp();
235         mSpyContext = mComponentContextFixture.getTestDouble().getApplicationContext();
236         doReturn(mSpyContext).when(mSpyContext).getApplicationContext();
237 
238         mNumOutgoingCallsMade = 0;
239 
240         // First set up information about the In-Call services in the mock Context, since
241         // Telecom will search for these as soon as it is instantiated
242         setupInCallServices();
243 
244         // Next, create the TelecomSystem, our system under test
245         setupTelecomSystem();
246 
247         // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
248         // now-running TelecomSystem
249         setupConnectionServices();
250     }
251 
252     @Override
tearDown()253     public void tearDown() throws Exception {
254         mTelecomSystem = null;
255         super.tearDown();
256     }
257 
makeConferenceCall()258     protected ParcelableCall makeConferenceCall() throws Exception {
259         IdPair callId1 = startAndMakeActiveOutgoingCall("650-555-1212",
260                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
261 
262         IdPair callId2 = startAndMakeActiveOutgoingCall("650-555-1213",
263                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
264 
265         IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter();
266         inCallAdapter.conference(callId1.mCallId, callId2.mCallId);
267         // Wait for wacky non-deterministic behavior
268         Thread.sleep(200);
269         ParcelableCall call1 = mInCallServiceFixtureX.getCall(callId1.mCallId);
270         ParcelableCall call2 = mInCallServiceFixtureX.getCall(callId2.mCallId);
271         // Check that the two calls end up with a parent in the end
272         assertNotNull(call1.getParentCallId());
273         assertNotNull(call2.getParentCallId());
274         assertEquals(call1.getParentCallId(), call2.getParentCallId());
275 
276         // Check to make sure that the parent call made it to the in-call service
277         String parentCallId = call1.getParentCallId();
278         ParcelableCall conferenceCall = mInCallServiceFixtureX.getCall(parentCallId);
279         assertEquals(2, conferenceCall.getChildCallIds().size());
280         assertTrue(conferenceCall.getChildCallIds().contains(callId1.mCallId));
281         assertTrue(conferenceCall.getChildCallIds().contains(callId2.mCallId));
282         return conferenceCall;
283     }
284 
setupTelecomSystem()285     private void setupTelecomSystem() throws Exception {
286         // Use actual implementations instead of mocking the interface out.
287         HeadsetMediaButtonFactory headsetMediaButtonFactory =
288                 spy(new HeadsetMediaButtonFactoryF());
289         ProximitySensorManagerFactory proximitySensorManagerFactory =
290                 spy(new ProximitySensorManagerFactoryF());
291         InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
292                 spy(new InCallWakeLockControllerFactoryF());
293         mAudioService = setupAudioService();
294 
295         mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
296 
297         mTimeoutsAdapter = mock(Timeouts.Adapter.class);
298         when(mTimeoutsAdapter.getCallScreeningTimeoutMillis(any(ContentResolver.class)))
299                 .thenReturn(TEST_TIMEOUT / 10L);
300 
301         mTelecomSystem = new TelecomSystem(
302                 mComponentContextFixture.getTestDouble(),
303                 new MissedCallNotifierImplFactory() {
304                     @Override
305                     public MissedCallNotifier makeMissedCallNotifierImpl(Context context,
306                             PhoneAccountRegistrar phoneAccountRegistrar) {
307                         return mMissedCallNotifier;
308                     }
309                 },
310                 mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
311                 headsetMediaButtonFactory,
312                 proximitySensorManagerFactory,
313                 inCallWakeLockControllerFactory,
314                 new CallAudioManager.AudioServiceFactory() {
315                     @Override
316                     public IAudioService getAudioService() {
317                         return mAudioService;
318                     }
319                 },
320                 new BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory() {
321                     @Override
322                     public BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
323                             TelecomSystem.SyncRoot lock, CallsManager callsManager,
324                             PhoneAccountRegistrar phoneAccountRegistrar) {
325                         return mBluetoothPhoneServiceImpl;
326                     }
327                 },
328                 mTimeoutsAdapter,
329                 mAsyncRingtonePlayer);
330 
331         mComponentContextFixture.setTelecomManager(new TelecomManager(
332                 mComponentContextFixture.getTestDouble(),
333                 mTelecomSystem.getTelecomServiceImpl().getBinder()));
334 
335         verify(headsetMediaButtonFactory).create(
336                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
337                 any(CallsManager.class),
338                 any(TelecomSystem.SyncRoot.class));
339         verify(proximitySensorManagerFactory).create(
340                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
341                 any(CallsManager.class));
342         verify(inCallWakeLockControllerFactory).create(
343                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
344                 any(CallsManager.class));
345     }
346 
setupConnectionServices()347     private void setupConnectionServices() throws Exception {
348         mConnectionServiceFixtureA = new ConnectionServiceFixture();
349         mConnectionServiceFixtureB = new ConnectionServiceFixture();
350 
351         mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameA,
352                 mConnectionServiceFixtureA.getTestDouble());
353         mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameB,
354                 mConnectionServiceFixtureB.getTestDouble());
355 
356         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
357         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
358         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
359 
360         mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
361                 mPhoneAccountA0.getAccountHandle(), Process.myUserHandle());
362     }
363 
setupInCallServices()364     private void setupInCallServices() throws Exception {
365         mComponentContextFixture.putResource(
366                 com.android.server.telecom.R.string.ui_default_package,
367                 mInCallServiceComponentNameX.getPackageName());
368         mComponentContextFixture.putResource(
369                 com.android.server.telecom.R.string.incall_default_class,
370                 mInCallServiceComponentNameX.getClassName());
371         mComponentContextFixture.putBooleanResource(
372                 com.android.internal.R.bool.config_voice_capable, true);
373 
374         mInCallServiceFixtureX = new InCallServiceFixture();
375         mInCallServiceFixtureY = new InCallServiceFixture();
376 
377         mComponentContextFixture.addInCallService(mInCallServiceComponentNameX,
378                 mInCallServiceFixtureX.getTestDouble());
379         mComponentContextFixture.addInCallService(mInCallServiceComponentNameY,
380                 mInCallServiceFixtureY.getTestDouble());
381     }
382 
383     /**
384      * Helper method for setting up the fake audio service.
385      * Calls to the fake audio service need to toggle the return
386      * value of AudioManager#isMicrophoneMute.
387      * @return mock of IAudioService
388      */
setupAudioService()389     private IAudioService setupAudioService() {
390         IAudioService audioService = mock(IAudioService.class);
391 
392         final AudioManager fakeAudioManager =
393                 (AudioManager) mComponentContextFixture.getTestDouble()
394                         .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
395 
396         try {
397             doAnswer(new Answer() {
398                 @Override
399                 public Object answer(InvocationOnMock i) {
400                     Object[] args = i.getArguments();
401                     doReturn(args[0]).when(fakeAudioManager).isMicrophoneMute();
402                     return null;
403                 }
404             }).when(audioService)
405                     .setMicrophoneMute(any(Boolean.class), any(String.class), any(Integer.class));
406 
407         } catch (android.os.RemoteException e) {
408             // Do nothing, leave the faked microphone state as-is
409         }
410         return audioService;
411     }
412 
startOutgoingPhoneCallWithNoPhoneAccount(String number, ConnectionServiceFixture connectionServiceFixture)413     protected String startOutgoingPhoneCallWithNoPhoneAccount(String number,
414             ConnectionServiceFixture connectionServiceFixture)
415             throws Exception {
416 
417         return startOutgoingPhoneCallPendingCreateConnection(number, null,
418                 connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
419     }
420 
outgoingCallPhoneAccountSelected(PhoneAccountHandle phoneAccountHandle, int startingNumConnections, int startingNumCalls, ConnectionServiceFixture connectionServiceFixture)421     protected IdPair outgoingCallPhoneAccountSelected(PhoneAccountHandle phoneAccountHandle,
422             int startingNumConnections, int startingNumCalls,
423             ConnectionServiceFixture connectionServiceFixture) throws Exception {
424 
425         IdPair ids = outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
426                 phoneAccountHandle, connectionServiceFixture);
427 
428         connectionServiceFixture.sendSetDialing(ids.mConnectionId);
429         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
430         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
431 
432         connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
433 
434         connectionServiceFixture.sendSetActive(ids.mConnectionId);
435         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
436         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
437 
438         return ids;
439     }
440 
startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser)441     protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
442             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser)
443             throws Exception {
444 
445         return startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
446                 initiatingUser, VideoProfile.STATE_AUDIO_ONLY);
447     }
448 
startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, int videoState)449     protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
450             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
451             int videoState) throws Exception {
452         int startingNumConnections = connectionServiceFixture.mConnectionById.size();
453         int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
454 
455         startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
456                 connectionServiceFixture, initiatingUser, videoState);
457 
458         return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
459                 phoneAccountHandle, connectionServiceFixture);
460     }
461 
startOutgoingPhoneCallPendingCreateConnection(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, int videoState)462     protected String startOutgoingPhoneCallPendingCreateConnection(String number,
463             PhoneAccountHandle phoneAccountHandle,
464             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
465             int videoState) throws Exception {
466         reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
467                 mInCallServiceFixtureY.getTestDouble());
468 
469         assertEquals(mInCallServiceFixtureX.mCallById.size(),
470                 mInCallServiceFixtureY.mCallById.size());
471         assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
472                 (mInCallServiceFixtureY.mInCallAdapter != null));
473 
474         mNumOutgoingCallsMade++;
475 
476         boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
477 
478         Intent actionCallIntent = new Intent();
479         actionCallIntent.setData(Uri.parse("tel:" + number));
480         actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
481         actionCallIntent.setAction(Intent.ACTION_CALL);
482         if (phoneAccountHandle != null) {
483             actionCallIntent.putExtra(
484                     TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
485                     phoneAccountHandle);
486         }
487         if (videoState != VideoProfile.STATE_AUDIO_ONLY) {
488             actionCallIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
489         }
490 
491         final UserHandle userHandle = initiatingUser;
492         Context localAppContext = mComponentContextFixture.getTestDouble().getApplicationContext();
493         new UserCallIntentProcessor(localAppContext, userHandle).processIntent(
494                 actionCallIntent, null, true /* hasCallAppOp*/);
495         // UserCallIntentProcessor's mContext.sendBroadcastAsUser(...) will call to an empty method
496         // as to not actually try to send an intent to PrimaryCallReceiver. We verify that it was
497         // called correctly in order to continue.
498         verify(localAppContext).sendBroadcastAsUser(actionCallIntent, UserHandle.SYSTEM);
499         mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent);
500 
501         if (!hasInCallAdapter) {
502             verify(mInCallServiceFixtureX.getTestDouble())
503                     .setInCallAdapter(
504                             any(IInCallAdapter.class));
505             verify(mInCallServiceFixtureY.getTestDouble())
506                     .setInCallAdapter(
507                             any(IInCallAdapter.class));
508         }
509 
510         ArgumentCaptor<Intent> newOutgoingCallIntent =
511                 ArgumentCaptor.forClass(Intent.class);
512         ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
513                 ArgumentCaptor.forClass(BroadcastReceiver.class);
514 
515         verify(mComponentContextFixture.getTestDouble().getApplicationContext(),
516                 times(mNumOutgoingCallsMade))
517                 .sendOrderedBroadcastAsUser(
518                         newOutgoingCallIntent.capture(),
519                         any(UserHandle.class),
520                         anyString(),
521                         anyInt(),
522                         newOutgoingCallReceiver.capture(),
523                         any(Handler.class),
524                         anyInt(),
525                         anyString(),
526                         any(Bundle.class));
527 
528         // Pass on the new outgoing call Intent
529         // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
530         newOutgoingCallReceiver.getValue().setPendingResult(
531                 new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
532         newOutgoingCallReceiver.getValue().setResultData(
533                 newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
534         newOutgoingCallReceiver.getValue().onReceive(mComponentContextFixture.getTestDouble(),
535                 newOutgoingCallIntent.getValue());
536 
537         return mInCallServiceFixtureX.mLatestCallId;
538     }
539 
outgoingCallCreateConnectionComplete(int startingNumConnections, int startingNumCalls, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)540     protected IdPair outgoingCallCreateConnectionComplete(int startingNumConnections,
541             int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
542             ConnectionServiceFixture connectionServiceFixture) throws Exception {
543 
544         assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
545 
546         verify(connectionServiceFixture.getTestDouble())
547                 .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
548                         anyBoolean(), anyBoolean());
549         connectionServiceFixture.sendHandleCreateConnectionComplete(
550                 connectionServiceFixture.mLatestConnectionId);
551 
552         assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
553         assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
554 
555         assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
556 
557         return new IdPair(connectionServiceFixture.mLatestConnectionId,
558                 mInCallServiceFixtureX.mLatestCallId);
559     }
560 
startIncomingPhoneCall( String number, PhoneAccountHandle phoneAccountHandle, final ConnectionServiceFixture connectionServiceFixture)561     protected IdPair startIncomingPhoneCall(
562             String number,
563             PhoneAccountHandle phoneAccountHandle,
564             final ConnectionServiceFixture connectionServiceFixture) throws Exception {
565         return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
566                 connectionServiceFixture);
567     }
568 
startIncomingPhoneCall( String number, PhoneAccountHandle phoneAccountHandle, int videoState, final ConnectionServiceFixture connectionServiceFixture)569     protected IdPair startIncomingPhoneCall(
570             String number,
571             PhoneAccountHandle phoneAccountHandle,
572             int videoState,
573             final ConnectionServiceFixture connectionServiceFixture) throws Exception {
574         reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
575                 mInCallServiceFixtureY.getTestDouble());
576 
577         assertEquals(mInCallServiceFixtureX.mCallById.size(),
578                 mInCallServiceFixtureY.mCallById.size());
579         assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
580                 (mInCallServiceFixtureY.mInCallAdapter != null));
581         final int startingNumConnections = connectionServiceFixture.mConnectionById.size();
582         final int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
583         boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
584         connectionServiceFixture.mConnectionServiceDelegate.mVideoState = videoState;
585 
586         Bundle extras = new Bundle();
587         extras.putParcelable(
588                 TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
589                 Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
590         mTelecomSystem.getTelecomServiceImpl().getBinder()
591                 .addNewIncomingCall(phoneAccountHandle, extras);
592 
593         verify(connectionServiceFixture.getTestDouble())
594                 .createConnection(any(PhoneAccountHandle.class), anyString(),
595                         any(ConnectionRequest.class), eq(true), eq(false));
596 
597         for (CallerInfoAsyncQueryFactoryFixture.Request request :
598                 mCallerInfoAsyncQueryFactoryFixture.mRequests) {
599             request.reply();
600         }
601 
602         IContentProvider blockedNumberProvider =
603                 mSpyContext.getContentResolver().acquireProvider(BlockedNumberContract.AUTHORITY);
604         verify(blockedNumberProvider, timeout(TEST_TIMEOUT)).call(
605                 anyString(),
606                 eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
607                 eq(number),
608                 isNull(Bundle.class));
609 
610         // For the case of incoming calls, Telecom connecting the InCall services and adding the
611         // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
612         // is added, future interactions as triggered by the ConnectionService, through the various
613         // test fixtures, will be synchronous.
614 
615         if (!hasInCallAdapter) {
616             verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
617                     .setInCallAdapter(any(IInCallAdapter.class));
618             verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
619                     .setInCallAdapter(any(IInCallAdapter.class));
620         }
621 
622         // Give the InCallService time to respond
623 
624         assertTrueWithTimeout(new Predicate<Void>() {
625             @Override
626             public boolean apply(Void v) {
627                 return mInCallServiceFixtureX.mInCallAdapter != null;
628             }
629         });
630 
631         assertTrueWithTimeout(new Predicate<Void>() {
632             @Override
633             public boolean apply(Void v) {
634                 return mInCallServiceFixtureY.mInCallAdapter != null;
635             }
636         });
637 
638         verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
639                 .addCall(any(ParcelableCall.class));
640         verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
641                 .addCall(any(ParcelableCall.class));
642 
643         // Give the InCallService time to respond
644 
645         assertTrueWithTimeout(new Predicate<Void>() {
646             @Override
647             public boolean apply(Void v) {
648                 return startingNumConnections + 1 ==
649                         connectionServiceFixture.mConnectionById.size();
650             }
651         });
652         assertTrueWithTimeout(new Predicate<Void>() {
653             @Override
654             public boolean apply(Void v) {
655                 return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
656             }
657         });
658         assertTrueWithTimeout(new Predicate<Void>() {
659             @Override
660             public boolean apply(Void v) {
661                 return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
662             }
663         });
664 
665         assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
666 
667         return new IdPair(connectionServiceFixture.mLatestConnectionId,
668                 mInCallServiceFixtureX.mLatestCallId);
669     }
670 
startAndMakeActiveOutgoingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)671     protected IdPair startAndMakeActiveOutgoingCall(
672             String number,
673             PhoneAccountHandle phoneAccountHandle,
674             ConnectionServiceFixture connectionServiceFixture) throws Exception {
675         return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
676                 VideoProfile.STATE_AUDIO_ONLY);
677     }
678 
679     // A simple outgoing call, verifying that the appropriate connection service is contacted,
680     // the proper lifecycle is followed, and both In-Call Services are updated correctly.
startAndMakeActiveOutgoingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, int videoState)681     protected IdPair startAndMakeActiveOutgoingCall(
682             String number,
683             PhoneAccountHandle phoneAccountHandle,
684             ConnectionServiceFixture connectionServiceFixture, int videoState) throws Exception {
685         IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
686                 Process.myUserHandle(), videoState);
687 
688         connectionServiceFixture.sendSetDialing(ids.mConnectionId);
689         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
690         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
691 
692         connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
693 
694         connectionServiceFixture.sendSetActive(ids.mConnectionId);
695         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
696         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
697 
698         return ids;
699     }
700 
startAndMakeActiveIncomingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)701     protected IdPair startAndMakeActiveIncomingCall(
702             String number,
703             PhoneAccountHandle phoneAccountHandle,
704             ConnectionServiceFixture connectionServiceFixture) throws Exception {
705         return startAndMakeActiveIncomingCall(number, phoneAccountHandle, connectionServiceFixture,
706                 VideoProfile.STATE_AUDIO_ONLY);
707     }
708 
709     // A simple incoming call, similar in scope to the previous test
startAndMakeActiveIncomingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, int videoState)710     protected IdPair startAndMakeActiveIncomingCall(
711             String number,
712             PhoneAccountHandle phoneAccountHandle,
713             ConnectionServiceFixture connectionServiceFixture,
714             int videoState) throws Exception {
715         IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
716 
717         assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
718         assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
719 
720         mInCallServiceFixtureX.mInCallAdapter
721                 .answerCall(ids.mCallId, videoState);
722 
723         if (!VideoProfile.isVideo(videoState)) {
724             verify(connectionServiceFixture.getTestDouble())
725                     .answer(ids.mConnectionId);
726         } else {
727             verify(connectionServiceFixture.getTestDouble())
728                     .answerVideo(ids.mConnectionId, videoState);
729         }
730 
731         connectionServiceFixture.sendSetActive(ids.mConnectionId);
732         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
733         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
734 
735         return ids;
736     }
737 
assertTrueWithTimeout(Predicate<Void> predicate)738     protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
739         int elapsed = 0;
740         while (elapsed < TEST_TIMEOUT) {
741             if (predicate.apply(null)) {
742                 return;
743             } else {
744                 try {
745                     Thread.sleep(TEST_POLL_INTERVAL);
746                     elapsed += TEST_POLL_INTERVAL;
747                 } catch (InterruptedException e) {
748                     fail(e.toString());
749                 }
750             }
751         }
752         fail("Timeout in assertTrueWithTimeout");
753     }
754 }
755