1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.telecom.tests;
18 
19 import com.android.internal.telecom.IConnectionService;
20 import com.android.internal.telecom.IConnectionServiceAdapter;
21 import com.android.internal.telecom.IVideoProvider;
22 import com.android.internal.telecom.RemoteServiceCallback;
23 
24 import junit.framework.TestCase;
25 
26 import org.mockito.Mockito;
27 
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.IBinder;
33 import android.os.IInterface;
34 import android.os.ParcelFileDescriptor;
35 import android.os.RemoteException;
36 import android.telecom.CallAudioState;
37 import android.telecom.CallEndpoint;
38 import android.telecom.CallScreeningService;
39 import android.telecom.Conference;
40 import android.telecom.Connection;
41 import android.telecom.ConnectionRequest;
42 import android.telecom.ConnectionService;
43 import android.telecom.DisconnectCause;
44 import android.telecom.Log;
45 import android.telecom.Logging.Session;
46 import android.telecom.ParcelableConference;
47 import android.telecom.ParcelableConnection;
48 import android.telecom.PhoneAccountHandle;
49 import android.telecom.StatusHints;
50 import android.telecom.TelecomManager;
51 
52 import com.google.android.collect.Lists;
53 
54 import java.lang.Override;
55 import java.util.ArrayList;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61 import java.util.concurrent.CountDownLatch;
62 import java.util.concurrent.TimeUnit;
63 
64 /**
65  * Controls a test {@link IConnectionService} as would be provided by a source of connectivity
66  * to the Telecom framework.
67  */
68 public class ConnectionServiceFixture implements TestFixture<IConnectionService> {
69     static int INVALID_VIDEO_STATE = -1;
70     public CountDownLatch mExtrasLock = new CountDownLatch(1);
71     static int NOT_SPECIFIED = 0;
72     public static final String STATUS_HINTS_EXTRA = "updateStatusHints";
73     public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
74             new PhoneAccountHandle(new ComponentName("pkg", "cls"), "test");
75 
76     /**
77      * Implementation of ConnectionService that performs no-ops for tasks normally meant for
78      * Telephony and reports success back to Telecom
79      */
80     public class FakeConnectionServiceDelegate extends ConnectionService {
81         int mVideoState = INVALID_VIDEO_STATE;
82         int mCapabilities = NOT_SPECIFIED;
83         int mProperties = NOT_SPECIFIED;
84 
FakeConnectionServiceDelegate(Context base)85         public FakeConnectionServiceDelegate(Context base) {
86             attachBaseContext(base);
87         }
88 
89         @Override
onCreateUnknownConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)90         public Connection onCreateUnknownConnection(
91                 PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
92             mLatestConnection = new FakeConnection(request.getVideoState(), request.getAddress());
93             return mLatestConnection;
94         }
95 
96         @Override
onCreateIncomingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)97         public Connection onCreateIncomingConnection(
98                 PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
99             FakeConnection fakeConnection =  new FakeConnection(
100                     mVideoState == INVALID_VIDEO_STATE ? request.getVideoState() : mVideoState,
101                     request.getAddress());
102             mLatestConnection = fakeConnection;
103             if (mCapabilities != NOT_SPECIFIED) {
104                 fakeConnection.setConnectionCapabilities(mCapabilities);
105             }
106             if (mProperties != NOT_SPECIFIED) {
107                 fakeConnection.setConnectionProperties(mProperties);
108             }
109             // Testing for StatusHints image icon cross user access
110             if (request.getExtras() != null) {
111                 fakeConnection.setStatusHints(
112                         request.getExtras().getParcelable(STATUS_HINTS_EXTRA));
113             }
114 
115             return fakeConnection;
116         }
117 
118         @Override
onCreateOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)119         public Connection onCreateOutgoingConnection(
120                 PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
121             FakeConnection fakeConnection = new FakeConnection(request.getVideoState(),
122                     request.getAddress());
123             mLatestConnection = fakeConnection;
124             if (mCapabilities != NOT_SPECIFIED) {
125                 fakeConnection.setConnectionCapabilities(mCapabilities);
126             }
127             if (mProperties != NOT_SPECIFIED) {
128                 fakeConnection.setConnectionProperties(mProperties);
129             }
130             // Testing for StatusHints image icon cross user access
131             if (request.getExtras() != null) {
132                 fakeConnection.setStatusHints(
133                         request.getExtras().getParcelable(STATUS_HINTS_EXTRA));
134             }
135             return fakeConnection;
136         }
137 
138         @Override
onCreateConnectionComplete(Connection connection)139         public void onCreateConnectionComplete(Connection connection) {
140         }
141 
142         @Override
onConference(Connection cxn1, Connection cxn2)143         public void onConference(Connection cxn1, Connection cxn2) {
144             if (((FakeConnection) cxn1).getIsConferenceCreated()) {
145                 // Usually, this is implemented by something in Telephony, which does a bunch of
146                 // radio work to conference the two connections together. Here we just short-cut
147                 // that and declare them conferenced.
148                 Conference fakeConference = new FakeConference();
149                 fakeConference.addConnection(cxn1);
150                 fakeConference.addConnection(cxn2);
151                 if (cxn1.getStatusHints() != null || cxn2.getStatusHints() != null) {
152                     // For testing purposes, pick one of the status hints that isn't null.
153                     StatusHints statusHints = cxn1.getStatusHints() != null
154                             ? cxn1.getStatusHints() : cxn2.getStatusHints();
155                     fakeConference.setStatusHints(statusHints);
156                 }
157                 mLatestConference = fakeConference;
158                 addConference(fakeConference);
159             } else {
160                 try {
161                     sendSetConferenceMergeFailed(cxn1.getTelecomCallId());
162                 } catch (Exception e) {
163                     Log.w(this, "Exception on sendSetConferenceMergeFailed: " + e.getMessage());
164                 }
165             }
166         }
167     }
168 
169     public class FakeConnection extends Connection {
170         // Set to false if you wish the Conference merge to fail.
171         boolean mIsConferenceCreated = true;
172 
FakeConnection(int videoState, Uri address)173         public FakeConnection(int videoState, Uri address) {
174             super();
175             int capabilities = getConnectionCapabilities();
176             capabilities |= CAPABILITY_MUTE;
177             capabilities |= CAPABILITY_SUPPORT_HOLD;
178             capabilities |= CAPABILITY_HOLD;
179             setVideoState(videoState);
180             setConnectionCapabilities(capabilities);
181             setDialing();
182             setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
183         }
184 
185         @Override
onExtrasChanged(Bundle extras)186         public void onExtrasChanged(Bundle extras) {
187             mExtrasLock.countDown();
188         }
189 
getIsConferenceCreated()190         public boolean getIsConferenceCreated() {
191             return mIsConferenceCreated;
192         }
193 
setIsConferenceCreated(boolean isConferenceCreated)194         public void setIsConferenceCreated(boolean isConferenceCreated) {
195             mIsConferenceCreated = isConferenceCreated;
196         }
197     }
198 
199     public class FakeConference extends Conference {
FakeConference()200         public FakeConference() {
201             super(TEST_PHONE_ACCOUNT_HANDLE);
202             setConnectionCapabilities(
203                     Connection.CAPABILITY_SUPPORT_HOLD
204                             | Connection.CAPABILITY_HOLD
205                             | Connection.CAPABILITY_MUTE
206                             | Connection.CAPABILITY_MANAGE_CONFERENCE);
207         }
208 
209         @Override
onMerge(Connection connection)210         public void onMerge(Connection connection) {
211             // Do nothing besides inform the connection that it was merged into this conference.
212             connection.setConference(this);
213         }
214 
215         @Override
onExtrasChanged(Bundle extras)216         public void onExtrasChanged(Bundle extras) {
217             Log.w(this, "FakeConference onExtrasChanged");
218             mExtrasLock.countDown();
219         }
220     }
221 
222     public class FakeConnectionService extends IConnectionService.Stub {
223         List<String> rejectedCallIds = Lists.newArrayList();
224 
225         @Override
addConnectionServiceAdapter(IConnectionServiceAdapter adapter, Session.Info info)226         public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter,
227                 Session.Info info) throws RemoteException {
228             if (!mConnectionServiceAdapters.add(adapter)) {
229                 throw new RuntimeException("Adapter already added: " + adapter);
230             }
231             mConnectionServiceDelegateAdapter.addConnectionServiceAdapter(adapter,
232                     null /*Session.Info*/);
233         }
234 
235         @Override
removeConnectionServiceAdapter(IConnectionServiceAdapter adapter, Session.Info info)236         public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter,
237                 Session.Info info) throws RemoteException {
238             if (!mConnectionServiceAdapters.remove(adapter)) {
239                 throw new RuntimeException("Adapter never added: " + adapter);
240             }
241             mConnectionServiceDelegateAdapter.removeConnectionServiceAdapter(adapter,
242                     null /*Session.Info*/);
243         }
244 
245         @Override
createConnection(PhoneAccountHandle connectionManagerPhoneAccount, String id, ConnectionRequest request, boolean isIncoming, boolean isUnknown, Session.Info info)246         public void createConnection(PhoneAccountHandle connectionManagerPhoneAccount,
247                 String id, ConnectionRequest request, boolean isIncoming, boolean isUnknown,
248                 Session.Info info) throws RemoteException {
249             Log.i(ConnectionServiceFixture.this, "createConnection --> " + id);
250 
251             if (mConnectionById.containsKey(id)) {
252                 throw new RuntimeException("Connection already exists: " + id);
253             }
254             mLatestConnectionId = id;
255             ConnectionInfo c = new ConnectionInfo();
256             c.connectionManagerPhoneAccount = connectionManagerPhoneAccount;
257             c.id = id;
258             c.request = request;
259             c.isIncoming = isIncoming;
260             c.isUnknown = isUnknown;
261             c.capabilities |= Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD;
262             c.videoState = request.getVideoState();
263             c.mockVideoProvider = new MockVideoProvider();
264             c.videoProvider = c.mockVideoProvider.getInterface();
265             c.isConferenceCreated = true;
266             mConnectionById.put(id, c);
267             mConnectionServiceDelegateAdapter.createConnection(connectionManagerPhoneAccount,
268                     id, request, isIncoming, isUnknown, null /*Session.Info*/);
269         }
270 
271         @Override
createConnectionComplete(String id, Session.Info info)272         public void createConnectionComplete(String id, Session.Info info) throws RemoteException {
273             Log.i(ConnectionServiceFixture.this, "createConnectionComplete: %s", id);
274             mConnectionServiceDelegateAdapter.createConnectionComplete(id, null /*Session.Info*/);
275         }
276 
277         @Override
createConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, String callId, ConnectionRequest request, boolean isIncoming, Session.Info sessionInfo)278         public void createConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
279                 String callId, ConnectionRequest request, boolean isIncoming,
280                 Session.Info sessionInfo) throws RemoteException {
281             Log.i(ConnectionServiceFixture.this, "createConnectionFailed --> " + callId);
282 
283             if (mConnectionById.containsKey(callId)) {
284                 throw new RuntimeException("Connection already exists: " + callId);
285             }
286 
287             // TODO(3p-calls): Implement this.
288         }
289 
290         @Override
createConference( PhoneAccountHandle connectionManagerPhoneAccount, String id, ConnectionRequest request, boolean isIncoming, boolean isUnknown, Session.Info sessionInfo)291         public void createConference(
292                 PhoneAccountHandle connectionManagerPhoneAccount,
293                 String id,
294                 ConnectionRequest request,
295                 boolean isIncoming,
296                 boolean isUnknown,
297                 Session.Info sessionInfo) { }
298 
299         @Override
createConferenceComplete(String id, Session.Info sessionInfo)300         public void createConferenceComplete(String id, Session.Info sessionInfo) { }
301 
302         @Override
createConferenceFailed( PhoneAccountHandle connectionManagerPhoneAccount, String callId, ConnectionRequest request, boolean isIncoming, Session.Info sessionInfo)303         public void createConferenceFailed(
304                 PhoneAccountHandle connectionManagerPhoneAccount,
305                 String callId,
306                 ConnectionRequest request,
307                 boolean isIncoming,
308                 Session.Info sessionInfo) { }
309 
310         @Override
abort(String callId, Session.Info info)311         public void abort(String callId, Session.Info info) throws RemoteException { }
312 
313         @Override
answerVideo(String callId, int videoState, Session.Info info)314         public void answerVideo(String callId, int videoState,
315                 Session.Info info) throws RemoteException { }
316 
317         @Override
answer(String callId, Session.Info info)318         public void answer(String callId, Session.Info info) throws RemoteException { }
319 
320         @Override
deflect(String callId, Uri address, Session.Info info)321         public void deflect(String callId, Uri address, Session.Info info)
322                 throws RemoteException { }
323 
324         @Override
transfer(String callId, Uri number, boolean isConfirmationRequired, Session.Info info)325         public void transfer(String callId, Uri number, boolean isConfirmationRequired,
326                 Session.Info info) throws RemoteException { }
327 
328         @Override
consultativeTransfer(String callId, String otherCallId, Session.Info info)329         public void consultativeTransfer(String callId, String otherCallId,
330                 Session.Info info) throws RemoteException { }
331 
332         @Override
reject(String callId, Session.Info info)333         public void reject(String callId, Session.Info info) throws RemoteException {
334             rejectedCallIds.add(callId);
335         }
336 
rejectWithReason(java.lang.String callId, int rejectReason, android.telecom.Logging.Session.Info sessionInfo)337         @Override public void rejectWithReason(java.lang.String callId, int rejectReason,
338                 android.telecom.Logging.Session.Info sessionInfo) throws RemoteException {
339             rejectedCallIds.add(callId);
340         }
341 
342         @Override
rejectWithMessage(String callId, String message, Session.Info info)343         public void rejectWithMessage(String callId, String message,
344                 Session.Info info) throws RemoteException {
345             rejectedCallIds.add(callId);
346         }
347 
348         @Override
disconnect(String callId, Session.Info info)349         public void disconnect(String callId, Session.Info info) throws RemoteException { }
350 
351         @Override
silence(String callId, Session.Info info)352         public void silence(String callId, Session.Info info) throws RemoteException { }
353 
354         @Override
hold(String callId, Session.Info info)355         public void hold(String callId, Session.Info info) throws RemoteException { }
356 
357         @Override
unhold(String callId, Session.Info info)358         public void unhold(String callId, Session.Info info) throws RemoteException { }
359 
360         @Override
onCallAudioStateChanged(String activeCallId, CallAudioState audioState, Session.Info info)361         public void onCallAudioStateChanged(String activeCallId, CallAudioState audioState,
362                 Session.Info info)
363                 throws RemoteException { }
364 
365         @Override
onCallEndpointChanged(String callId, CallEndpoint callEndpoint, Session.Info sessionInfo)366         public void onCallEndpointChanged(String callId, CallEndpoint callEndpoint,
367                 Session.Info sessionInfo) { }
368 
369         @Override
onAvailableCallEndpointsChanged(String callId, List<CallEndpoint> availableCallEndpoints, Session.Info sessionInfo)370         public void onAvailableCallEndpointsChanged(String callId,
371                 List<CallEndpoint> availableCallEndpoints, Session.Info sessionInfo) { }
372 
373         @Override
onMuteStateChanged(String callId, boolean isMuted, Session.Info sessionInfo)374         public void onMuteStateChanged(String callId, boolean isMuted,
375                 Session.Info sessionInfo) { }
376 
377         @Override
onUsingAlternativeUi(String activeCallId, boolean usingAlternativeUi, Session.Info info)378         public void onUsingAlternativeUi(String activeCallId, boolean usingAlternativeUi,
379                 Session.Info info) throws RemoteException { }
380 
381         @Override
onTrackedByNonUiService(String activeCallId, boolean isTracked, Session.Info info)382         public void onTrackedByNonUiService(String activeCallId, boolean isTracked,
383                 Session.Info info) throws RemoteException { }
384 
385         @Override
playDtmfTone(String callId, char digit, Session.Info info)386         public void playDtmfTone(String callId, char digit,
387                 Session.Info info) throws RemoteException { }
388 
389         @Override
stopDtmfTone(String callId, Session.Info info)390         public void stopDtmfTone(String callId, Session.Info info) throws RemoteException { }
391 
392         @Override
conference(String conferenceCallId, String callId, Session.Info info)393         public void conference(String conferenceCallId, String callId,
394                 Session.Info info) throws RemoteException {
395             mConnectionServiceDelegateAdapter.conference(conferenceCallId, callId, info);
396         }
397 
398         @Override
splitFromConference(String callId, Session.Info info)399         public void splitFromConference(String callId, Session.Info info) throws RemoteException { }
400 
401         @Override
mergeConference(String conferenceCallId, Session.Info info)402         public void mergeConference(String conferenceCallId,
403                 Session.Info info) throws RemoteException { }
404 
405         @Override
swapConference(String conferenceCallId, Session.Info info)406         public void swapConference(String conferenceCallId,
407                 Session.Info info) throws RemoteException { }
408 
409         @Override
addConferenceParticipants(String CallId, List<Uri> participants, Session.Info sessionInfo)410         public void addConferenceParticipants(String CallId, List<Uri> participants,
411                 Session.Info sessionInfo) throws RemoteException {
412 
413         }
414 
415         @Override
onPostDialContinue(String callId, boolean proceed, Session.Info info)416         public void onPostDialContinue(String callId, boolean proceed,
417                 Session.Info info) throws RemoteException { }
418 
419         @Override
pullExternalCall(String callId, Session.Info info)420         public void pullExternalCall(String callId, Session.Info info) throws RemoteException { }
421 
422         @Override
sendCallEvent(String callId, String event, Bundle extras, Session.Info info)423         public void sendCallEvent(String callId, String event, Bundle extras,
424                 Session.Info info) throws RemoteException
425         {}
426 
onExtrasChanged(String callId, Bundle extras, Session.Info info)427         public void onExtrasChanged(String callId, Bundle extras,
428                 Session.Info info) throws RemoteException {
429             mConnectionServiceDelegateAdapter.onExtrasChanged(callId, extras, info);
430         }
431 
432         @Override
startRtt(String callId, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall, Session.Info sessionInfo)433         public void startRtt(String callId, ParcelFileDescriptor fromInCall,
434                 ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException {
435 
436         }
437 
438         @Override
stopRtt(String callId, Session.Info sessionInfo)439         public void stopRtt(String callId, Session.Info sessionInfo) throws RemoteException {
440 
441         }
442 
443         @Override
respondToRttUpgradeRequest(String callId, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall, Session.Info sessionInfo)444         public void respondToRttUpgradeRequest(String callId, ParcelFileDescriptor fromInCall,
445                 ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException {
446 
447         }
448 
449         @Override
connectionServiceFocusLost(Session.Info sessionInfo)450         public void connectionServiceFocusLost(Session.Info sessionInfo) throws RemoteException {
451         }
452 
453         @Override
connectionServiceFocusGained(Session.Info sessionInfo)454         public void connectionServiceFocusGained(Session.Info sessionInfo) throws RemoteException {
455         }
456 
457         @Override
asBinder()458         public IBinder asBinder() {
459             return this;
460         }
461 
462         @Override
queryLocalInterface(String descriptor)463         public IInterface queryLocalInterface(String descriptor) {
464             return this;
465         }
466 
467         @Override
handoverFailed(String callId, ConnectionRequest request, int error, Session.Info sessionInfo)468         public void handoverFailed(String callId, ConnectionRequest request,
469                                    int error, Session.Info sessionInfo) {}
470 
471         @Override
handoverComplete(String callId, Session.Info sessionInfo)472         public void handoverComplete(String callId, Session.Info sessionInfo) {}
473 
474         @Override
onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo completionInfo, Session.Info sessionInfo)475         public void onCallFilteringCompleted(String callId,
476                 Connection.CallFilteringCompletionInfo completionInfo, Session.Info sessionInfo) { }
477     }
478 
479     FakeConnectionServiceDelegate mConnectionServiceDelegate;
480     private IConnectionService mConnectionServiceDelegateAdapter;
481 
482     FakeConnectionService mConnectionService = new FakeConnectionService();
483     private IConnectionService.Stub mConnectionServiceSpy = Mockito.spy(mConnectionService);
484 
485     public class ConnectionInfo {
486         PhoneAccountHandle connectionManagerPhoneAccount;
487         String id;
488         boolean ringing;
489         ConnectionRequest request;
490         boolean isIncoming;
491         boolean isUnknown;
492         int state;
493         int addressPresentation;
494         int capabilities;
495         int properties;
496         int supportedAudioRoutes;
497         StatusHints statusHints;
498         DisconnectCause disconnectCause;
499         String conferenceId;
500         String callerDisplayName;
501         int callerDisplayNamePresentation;
502         final List<String> conferenceableConnectionIds = new ArrayList<>();
503         IVideoProvider videoProvider;
504         Connection.VideoProvider videoProviderImpl;
505         MockVideoProvider mockVideoProvider;
506         int videoState;
507         boolean isVoipAudioMode;
508         Bundle extras;
509         boolean isConferenceCreated;
510         int callerNumberVerificationStatus;
511     }
512 
513     public class ConferenceInfo {
514         PhoneAccountHandle phoneAccount;
515         int state;
516         int capabilities;
517         int properties;
518         final List<String> connectionIds = new ArrayList<>();
519         IVideoProvider videoProvider;
520         int videoState;
521         long connectTimeMillis;
522         long connectElapsedTimeMillis;
523         StatusHints statusHints;
524         Bundle extras;
525     }
526 
527     public String mLatestConnectionId;
528     public Connection mLatestConnection;
529     public ParcelableConnection mLatestParcelableConnection;
530     public Conference mLatestConference;
531     public final Set<IConnectionServiceAdapter> mConnectionServiceAdapters = new HashSet<>();
532     public final Map<String, ConnectionInfo> mConnectionById = new HashMap<>();
533     public final Map<String, ConferenceInfo> mConferenceById = new HashMap<>();
534     public final List<ComponentName> mRemoteConnectionServiceNames = new ArrayList<>();
535     public final List<IBinder> mRemoteConnectionServices = new ArrayList<>();
536 
ConnectionServiceFixture(Context context)537     public ConnectionServiceFixture(Context context) throws Exception {
538         mConnectionServiceDelegate = new FakeConnectionServiceDelegate(context);
539         mConnectionServiceDelegateAdapter = IConnectionService.Stub.asInterface(
540                 mConnectionServiceDelegate.onBind(null));
541     }
542 
543     @Override
getTestDouble()544     public IConnectionService getTestDouble() {
545         return mConnectionServiceSpy;
546     }
547 
sendHandleCreateConnectionComplete(String id)548     public void sendHandleCreateConnectionComplete(String id) throws Exception {
549         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
550             a.handleCreateConnectionComplete(
551                     id,
552                     mConnectionById.get(id).request,
553                     parcelable(mConnectionById.get(id)), null /*Session.Info*/);
554         }
555     }
556 
sendSetActive(String id)557     public void sendSetActive(String id) throws Exception {
558         mConnectionById.get(id).state = Connection.STATE_ACTIVE;
559         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
560             a.setActive(id, null /*Session.Info*/);
561         }
562     }
563 
sendSetRinging(String id)564     public void sendSetRinging(String id) throws Exception {
565         mConnectionById.get(id).state = Connection.STATE_RINGING;
566         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
567             a.setRinging(id, null /*Session.Info*/);
568         }
569     }
570 
sendSetDialing(String id)571     public void sendSetDialing(String id) throws Exception {
572         mConnectionById.get(id).state = Connection.STATE_DIALING;
573         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
574             a.setDialing(id, null /*Session.Info*/);
575         }
576     }
577 
sendSetDisconnected(String id, int disconnectCause)578     public void sendSetDisconnected(String id, int disconnectCause) throws Exception {
579         mConnectionById.get(id).state = Connection.STATE_DISCONNECTED;
580         mConnectionById.get(id).disconnectCause = new DisconnectCause(disconnectCause);
581         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
582             a.setDisconnected(id, mConnectionById.get(id).disconnectCause, null /*Session.Info*/);
583         }
584     }
585 
sendSetOnHold(String id)586     public void sendSetOnHold(String id) throws Exception {
587         mConnectionById.get(id).state = Connection.STATE_HOLDING;
588         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
589             a.setOnHold(id, null /*Session.Info*/);
590         }
591     }
592 
sendSetRingbackRequested(String id)593     public void sendSetRingbackRequested(String id) throws Exception {
594         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
595             a.setRingbackRequested(id, mConnectionById.get(id).ringing, null /*Session.Info*/);
596         }
597     }
598 
sendSetConnectionCapabilities(String id)599     public void sendSetConnectionCapabilities(String id) throws Exception {
600         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
601             a.setConnectionCapabilities(id, mConnectionById.get(id).capabilities,
602                     null /*Session.Info*/);
603         }
604     }
605 
sendSetConnectionProperties(String id)606     public void sendSetConnectionProperties(String id) throws Exception {
607         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
608             a.setConnectionProperties(id, mConnectionById.get(id).properties, null /*Session.Info*/);
609         }
610     }
sendSetIsConferenced(String id)611     public void sendSetIsConferenced(String id) throws Exception {
612         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
613             a.setIsConferenced(id, mConnectionById.get(id).conferenceId, null /*Session.Info*/);
614         }
615     }
616 
sendAddConferenceCall(String id)617     public void sendAddConferenceCall(String id) throws Exception {
618         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
619             a.addConferenceCall(id, parcelable(mConferenceById.get(id)), null /*Session.Info*/);
620         }
621     }
622 
sendRemoveCall(String id)623     public void sendRemoveCall(String id) throws Exception {
624         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
625             a.removeCall(id, null /*Session.Info*/);
626         }
627     }
628 
sendOnPostDialWait(String id, String remaining)629     public void sendOnPostDialWait(String id, String remaining) throws Exception {
630         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
631             a.onPostDialWait(id, remaining, null /*Session.Info*/);
632         }
633     }
634 
sendOnPostDialChar(String id, char nextChar)635     public void sendOnPostDialChar(String id, char nextChar) throws Exception {
636         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
637             a.onPostDialChar(id, nextChar, null /*Session.Info*/);
638         }
639     }
640 
sendQueryRemoteConnectionServices()641     public void sendQueryRemoteConnectionServices() throws Exception {
642         mRemoteConnectionServices.clear();
643         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
644             a.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
645                 @Override
646                 public void onError() throws RemoteException {
647                     throw new RuntimeException();
648                 }
649 
650                 @Override
651                 public void onResult(
652                         List<ComponentName> names,
653                         List<IBinder> services)
654                         throws RemoteException {
655                     TestCase.assertEquals(names.size(), services.size());
656                     mRemoteConnectionServiceNames.addAll(names);
657                     mRemoteConnectionServices.addAll(services);
658                 }
659 
660                 @Override
661                 public IBinder asBinder() {
662                     return this;
663                 }
664             }, "" /* callingPackage */, null /*Session.Info*/);
665         }
666     }
667 
sendSetVideoProvider(String id)668     public void sendSetVideoProvider(String id) throws Exception {
669         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
670             a.setVideoProvider(id, mConnectionById.get(id).videoProvider, null /*Session.Info*/);
671         }
672     }
673 
sendSetVideoState(String id)674     public void sendSetVideoState(String id) throws Exception {
675         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
676             a.setVideoState(id, mConnectionById.get(id).videoState, null /*Session.Info*/);
677         }
678     }
679 
sendSetIsVoipAudioMode(String id)680     public void sendSetIsVoipAudioMode(String id) throws Exception {
681         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
682             a.setIsVoipAudioMode(id, mConnectionById.get(id).isVoipAudioMode,
683                     null /*Session.Info*/);
684         }
685     }
686 
sendSetStatusHints(String id)687     public void sendSetStatusHints(String id) throws Exception {
688         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
689             a.setStatusHints(id, mConnectionById.get(id).statusHints, null /*Session.Info*/);
690         }
691     }
692 
sendSetAddress(String id)693     public void sendSetAddress(String id) throws Exception {
694         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
695             a.setAddress(
696                     id,
697                     mConnectionById.get(id).request.getAddress(),
698                     mConnectionById.get(id).addressPresentation, null /*Session.Info*/);
699         }
700     }
701 
sendSetCallerDisplayName(String id)702     public void sendSetCallerDisplayName(String id) throws Exception {
703         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
704             a.setCallerDisplayName(
705                     id,
706                     mConnectionById.get(id).callerDisplayName,
707                     mConnectionById.get(id).callerDisplayNamePresentation, null /*Session.Info*/);
708         }
709     }
710 
sendSetConferenceableConnections(String id)711     public void sendSetConferenceableConnections(String id) throws Exception {
712         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
713             a.setConferenceableConnections(id, mConnectionById.get(id).conferenceableConnectionIds,
714                     null /*Session.Info*/);
715         }
716     }
717 
sendAddExistingConnection(String id)718     public void sendAddExistingConnection(String id) throws Exception {
719         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
720             a.addExistingConnection(id, parcelable(mConnectionById.get(id)), null /*Session.Info*/);
721         }
722     }
723 
sendConnectionEvent(String id, String event, Bundle extras)724     public void sendConnectionEvent(String id, String event, Bundle extras) throws Exception {
725         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
726             a.onConnectionEvent(id, event, extras, null /*Session.Info*/);
727         }
728     }
729 
sendSetConferenceMergeFailed(String id)730     public void sendSetConferenceMergeFailed(String id) throws Exception {
731         for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
732             a.setConferenceMergeFailed(id, null /*Session.Info*/);
733         }
734     }
735 
736     /**
737      * Waits until the {@link Connection#onExtrasChanged(Bundle)} API has been called on a
738      * {@link Connection} or {@link Conference}.
739      */
waitForExtras()740     public void waitForExtras() {
741         try {
742             mExtrasLock.await(TelecomSystemTest.TEST_TIMEOUT, TimeUnit.MILLISECONDS);
743         } catch (InterruptedException ie) {
744         }
745         mExtrasLock = new CountDownLatch(1);
746     }
747 
waitForHandlerToClear()748     public void waitForHandlerToClear() {
749         mConnectionServiceDelegate.getHandler().removeCallbacksAndMessages(null);
750         final CountDownLatch lock = new CountDownLatch(1);
751         mConnectionServiceDelegate.getHandler().post(lock::countDown);
752         while (lock.getCount() > 0) {
753             try {
754                 lock.await(TelecomSystemTest.TEST_TIMEOUT, TimeUnit.MILLISECONDS);
755             } catch (InterruptedException e) {
756                 // do nothing
757             }
758         }
759     }
760 
parcelable(ConferenceInfo c)761     private ParcelableConference parcelable(ConferenceInfo c) {
762         return new ParcelableConference.Builder(c.phoneAccount, c.state)
763                 .setConnectionCapabilities(c.capabilities)
764                 .setConnectionProperties(c.properties)
765                 .setConnectionIds(c.connectionIds)
766                 .setVideoAttributes(c.videoProvider, c.videoState)
767                 .setConnectTimeMillis(c.connectTimeMillis, c.connectElapsedTimeMillis)
768                 .setStatusHints(c.statusHints)
769                 .setExtras(c.extras)
770                 .build();
771     }
772 
parcelable(ConnectionInfo c)773     private ParcelableConnection parcelable(ConnectionInfo c) {
774         mLatestParcelableConnection = new ParcelableConnection(
775                 c.request.getAccountHandle(),
776                 c.state,
777                 c.capabilities,
778                 c.properties,
779                 c.supportedAudioRoutes,
780                 c.request.getAddress(),
781                 c.addressPresentation,
782                 c.callerDisplayName,
783                 c.callerDisplayNamePresentation,
784                 c.videoProvider,
785                 c.videoState,
786                 false, /* ringback requested */
787                 false, /* voip audio mode */
788                 0, /* Connect Time for conf call on this connection */
789                 0, /* Connect Real Time comes from conference call */
790                 c.statusHints,
791                 c.disconnectCause,
792                 c.conferenceableConnectionIds,
793                 c.extras,
794                 c.callerNumberVerificationStatus);
795         return mLatestParcelableConnection;
796     }
797 }
798