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