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