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