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