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