ConnectionServiceFixture.java revision b7853c0b716f9cc9ae174d1ca4efaa118ef1740e
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
17package com.android.server.telecom.tests;
18
19import com.android.internal.telecom.IConnectionService;
20import com.android.internal.telecom.IConnectionServiceAdapter;
21import com.android.internal.telecom.IVideoProvider;
22import com.android.internal.telecom.RemoteServiceCallback;
23import com.android.server.telecom.Log;
24
25import junit.framework.TestCase;
26
27import org.mockito.Mockito;
28
29import android.content.ComponentName;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.IBinder;
33import android.os.IInterface;
34import android.os.RemoteException;
35import android.telecom.CallAudioState;
36import android.telecom.Conference;
37import android.telecom.Connection;
38import android.telecom.ConnectionRequest;
39import android.telecom.ConnectionService;
40import android.telecom.DisconnectCause;
41import android.telecom.ParcelableConference;
42import android.telecom.ParcelableConnection;
43import android.telecom.PhoneAccountHandle;
44import android.telecom.StatusHints;
45import android.telecom.TelecomManager;
46
47import java.lang.Override;
48import java.util.ArrayList;
49import java.util.HashMap;
50import java.util.HashSet;
51import java.util.List;
52import java.util.Map;
53import java.util.Set;
54
55/**
56 * Controls a test {@link IConnectionService} as would be provided by a source of connectivity
57 * to the Telecom framework.
58 */
59public class ConnectionServiceFixture implements TestFixture<IConnectionService> {
60    static int INVALID_VIDEO_STATE = -1;
61
62    /**
63     * Implementation of ConnectionService that performs no-ops for tasks normally meant for
64     * Telephony and reports success back to Telecom
65     */
66    public class FakeConnectionServiceDelegate extends ConnectionService {
67        int mVideoState = INVALID_VIDEO_STATE;
68
69        @Override
70        public Connection onCreateUnknownConnection(
71                PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
72            return new FakeConnection(request.getVideoState(), request.getAddress());
73        }
74
75        @Override
76        public Connection onCreateIncomingConnection(
77                PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
78            return new FakeConnection(
79                    mVideoState == INVALID_VIDEO_STATE ? request.getVideoState() : mVideoState,
80                    request.getAddress());
81        }
82
83        @Override
84        public Connection onCreateOutgoingConnection(
85                PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
86            return new FakeConnection(request.getVideoState(), request.getAddress());
87        }
88
89        @Override
90        public void onConference(Connection cxn1, Connection cxn2) {
91            // Usually, this is implemented by something in Telephony, which does a bunch of radio
92            // work to conference the two connections together. Here we just short-cut that and
93            // declare them conferenced.
94            Conference fakeConference = new FakeConference();
95            fakeConference.addConnection(cxn1);
96            fakeConference.addConnection(cxn2);
97            addConference(fakeConference);
98        }
99    }
100
101    public class FakeConnection extends Connection {
102        public FakeConnection(int videoState, Uri address) {
103            super();
104            int capabilities = getConnectionCapabilities();
105            capabilities |= CAPABILITY_MUTE;
106            capabilities |= CAPABILITY_SUPPORT_HOLD;
107            capabilities |= CAPABILITY_HOLD;
108            setVideoState(videoState);
109            setConnectionCapabilities(capabilities);
110            setActive();
111            setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
112        }
113    }
114
115    public class FakeConference extends Conference {
116        public FakeConference() {
117            super(null);
118            setConnectionCapabilities(
119                    Connection.CAPABILITY_SUPPORT_HOLD
120                            | Connection.CAPABILITY_HOLD
121                            | Connection.CAPABILITY_MUTE
122                            | Connection.CAPABILITY_MANAGE_CONFERENCE);
123        }
124
125        @Override
126        public void onMerge(Connection connection) {
127            // Do nothing besides inform the connection that it was merged into this conference.
128            connection.setConference(this);
129        }
130    }
131
132    public class FakeConnectionService extends IConnectionService.Stub {
133
134        @Override
135        public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter)
136                throws RemoteException {
137            if (!mConnectionServiceAdapters.add(adapter)) {
138                throw new RuntimeException("Adapter already added: " + adapter);
139            }
140            mConnectionServiceDelegateAdapter.addConnectionServiceAdapter(adapter);
141        }
142
143        @Override
144        public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter)
145                throws RemoteException {
146            if (!mConnectionServiceAdapters.remove(adapter)) {
147                throw new RuntimeException("Adapter never added: " + adapter);
148            }
149            mConnectionServiceDelegateAdapter.removeConnectionServiceAdapter(adapter);
150        }
151
152        @Override
153        public void createConnection(PhoneAccountHandle connectionManagerPhoneAccount,
154                String id,
155                ConnectionRequest request, boolean isIncoming, boolean isUnknown)
156                throws RemoteException {
157            Log.i(ConnectionServiceFixture.this, "xoxox createConnection --> " + id);
158
159            if (mConnectionById.containsKey(id)) {
160                throw new RuntimeException("Connection already exists: " + id);
161            }
162            mLatestConnectionId = id;
163            ConnectionInfo c = new ConnectionInfo();
164            c.connectionManagerPhoneAccount = connectionManagerPhoneAccount;
165            c.id = id;
166            c.request = request;
167            c.isIncoming = isIncoming;
168            c.isUnknown = isUnknown;
169            c.capabilities |= Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD;
170            c.videoState = request.getVideoState();
171            c.mockVideoProvider = new MockVideoProvider();
172            c.videoProvider = c.mockVideoProvider.getInterface();
173            mConnectionById.put(id, c);
174            mConnectionServiceDelegateAdapter.createConnection(connectionManagerPhoneAccount,
175                    id, request, isIncoming, isUnknown);
176        }
177
178        @Override
179        public void abort(String callId) throws RemoteException { }
180
181        @Override
182        public void answerVideo(String callId, int videoState) throws RemoteException { }
183
184        @Override
185        public void answer(String callId) throws RemoteException { }
186
187        @Override
188        public void reject(String callId) throws RemoteException { }
189
190        @Override
191        public void rejectWithMessage(String callId, String message) throws RemoteException { }
192
193        @Override
194        public void disconnect(String callId) throws RemoteException { }
195
196        @Override
197        public void silence(String callId) throws RemoteException { }
198
199        @Override
200        public void hold(String callId) throws RemoteException { }
201
202        @Override
203        public void unhold(String callId) throws RemoteException { }
204
205        @Override
206        public void onCallAudioStateChanged(String activeCallId, CallAudioState audioState)
207                throws RemoteException { }
208
209        @Override
210        public void playDtmfTone(String callId, char digit) throws RemoteException { }
211
212        @Override
213        public void stopDtmfTone(String callId) throws RemoteException { }
214
215        @Override
216        public void conference(String conferenceCallId, String callId) throws RemoteException {
217            mConnectionServiceDelegateAdapter.conference(conferenceCallId, callId);
218        }
219
220        @Override
221        public void splitFromConference(String callId) throws RemoteException { }
222
223        @Override
224        public void mergeConference(String conferenceCallId) throws RemoteException { }
225
226        @Override
227        public void swapConference(String conferenceCallId) throws RemoteException { }
228
229        @Override
230        public void onPostDialContinue(String callId, boolean proceed) throws RemoteException { }
231
232        @Override
233        public IBinder asBinder() {
234            return this;
235        }
236
237        @Override
238        public IInterface queryLocalInterface(String descriptor) {
239            return this;
240        }
241    }
242
243    FakeConnectionServiceDelegate mConnectionServiceDelegate =
244            new FakeConnectionServiceDelegate();
245    private IConnectionService mConnectionServiceDelegateAdapter =
246            IConnectionService.Stub.asInterface(mConnectionServiceDelegate.onBind(null));
247
248    private IConnectionService.Stub mConnectionService = new FakeConnectionService();
249    private IConnectionService.Stub mConnectionServiceSpy = Mockito.spy(mConnectionService);
250
251    public class ConnectionInfo {
252        PhoneAccountHandle connectionManagerPhoneAccount;
253        String id;
254        boolean ringing;
255        ConnectionRequest request;
256        boolean isIncoming;
257        boolean isUnknown;
258        int state;
259        int addressPresentation;
260        int capabilities;
261        StatusHints statusHints;
262        DisconnectCause disconnectCause;
263        String conferenceId;
264        String callerDisplayName;
265        int callerDisplayNamePresentation;
266        final List<String> conferenceableConnectionIds = new ArrayList<>();
267        IVideoProvider videoProvider;
268        Connection.VideoProvider videoProviderImpl;
269        MockVideoProvider mockVideoProvider;
270        int videoState;
271        boolean isVoipAudioMode;
272        Bundle extras;
273    }
274
275    public class ConferenceInfo {
276        PhoneAccountHandle phoneAccount;
277        int state;
278        int capabilities;
279        final List<String> connectionIds = new ArrayList<>();
280        IVideoProvider videoProvider;
281        int videoState;
282        long connectTimeMillis;
283        StatusHints statusHints;
284        Bundle extras;
285    }
286
287    public String mLatestConnectionId;
288    public final Set<IConnectionServiceAdapter> mConnectionServiceAdapters = new HashSet<>();
289    public final Map<String, ConnectionInfo> mConnectionById = new HashMap<>();
290    public final Map<String, ConferenceInfo> mConferenceById = new HashMap<>();
291    public final List<ComponentName> mRemoteConnectionServiceNames = new ArrayList<>();
292    public final List<IBinder> mRemoteConnectionServices = new ArrayList<>();
293
294    public ConnectionServiceFixture() throws Exception { }
295
296    @Override
297    public IConnectionService getTestDouble() {
298        return mConnectionServiceSpy;
299    }
300
301    public void sendHandleCreateConnectionComplete(String id) throws Exception {
302        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
303            a.handleCreateConnectionComplete(
304                    id,
305                    mConnectionById.get(id).request,
306                    parcelable(mConnectionById.get(id)));
307        }
308    }
309
310    public void sendSetActive(String id) throws Exception {
311        mConnectionById.get(id).state = Connection.STATE_ACTIVE;
312        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
313            a.setActive(id);
314        }
315    }
316
317    public void sendSetRinging(String id) throws Exception {
318        mConnectionById.get(id).state = Connection.STATE_RINGING;
319        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
320            a.setRinging(id);
321        }
322    }
323
324    public void sendSetDialing(String id) throws Exception {
325        mConnectionById.get(id).state = Connection.STATE_DIALING;
326        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
327            a.setDialing(id);
328        }
329    }
330
331    public void sendSetDisconnected(String id, int disconnectCause) throws Exception {
332        mConnectionById.get(id).state = Connection.STATE_DISCONNECTED;
333        mConnectionById.get(id).disconnectCause = new DisconnectCause(disconnectCause);
334        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
335            a.setDisconnected(id, mConnectionById.get(id).disconnectCause);
336        }
337    }
338
339    public void sendSetOnHold(String id) throws Exception {
340        mConnectionById.get(id).state = Connection.STATE_HOLDING;
341        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
342            a.setOnHold(id);
343        }
344    }
345
346    public void sendSetRingbackRequested(String id) throws Exception {
347        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
348            a.setRingbackRequested(id, mConnectionById.get(id).ringing);
349        }
350    }
351
352    public void sendSetConnectionCapabilities(String id) throws Exception {
353        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
354            a.setConnectionCapabilities(id, mConnectionById.get(id).capabilities);
355        }
356    }
357
358    public void sendSetIsConferenced(String id) throws Exception {
359        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
360            a.setIsConferenced(id, mConnectionById.get(id).conferenceId);
361        }
362    }
363
364    public void sendAddConferenceCall(String id) throws Exception {
365        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
366            a.addConferenceCall(id, parcelable(mConferenceById.get(id)));
367        }
368    }
369
370    public void sendRemoveCall(String id) throws Exception {
371        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
372            a.removeCall(id);
373        }
374    }
375
376    public void sendOnPostDialWait(String id, String remaining) throws Exception {
377        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
378            a.onPostDialWait(id, remaining);
379        }
380    }
381
382    public void sendOnPostDialChar(String id, char nextChar) throws Exception {
383        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
384            a.onPostDialChar(id, nextChar);
385        }
386    }
387
388    public void sendQueryRemoteConnectionServices() throws Exception {
389        mRemoteConnectionServices.clear();
390        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
391            a.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
392                @Override
393                public void onError() throws RemoteException {
394                    throw new RuntimeException();
395                }
396
397                @Override
398                public void onResult(
399                        List<ComponentName> names,
400                        List<IBinder> services)
401                        throws RemoteException {
402                    TestCase.assertEquals(names.size(), services.size());
403                    mRemoteConnectionServiceNames.addAll(names);
404                    mRemoteConnectionServices.addAll(services);
405                }
406
407                @Override
408                public IBinder asBinder() {
409                    return this;
410                }
411            });
412        }
413    }
414
415    public void sendSetVideoProvider(String id) throws Exception {
416        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
417            a.setVideoProvider(id, mConnectionById.get(id).videoProvider);
418        }
419    }
420
421    public void sendSetVideoState(String id) throws Exception {
422        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
423            a.setVideoState(id, mConnectionById.get(id).videoState);
424        }
425    }
426
427    public void sendSetIsVoipAudioMode(String id) throws Exception {
428        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
429            a.setIsVoipAudioMode(id, mConnectionById.get(id).isVoipAudioMode);
430        }
431    }
432
433    public void sendSetStatusHints(String id) throws Exception {
434        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
435            a.setStatusHints(id, mConnectionById.get(id).statusHints);
436        }
437    }
438
439    public void sendSetAddress(String id) throws Exception {
440        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
441            a.setAddress(
442                    id,
443                    mConnectionById.get(id).request.getAddress(),
444                    mConnectionById.get(id).addressPresentation);
445        }
446    }
447
448    public void sendSetCallerDisplayName(String id) throws Exception {
449        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
450            a.setCallerDisplayName(
451                    id,
452                    mConnectionById.get(id).callerDisplayName,
453                    mConnectionById.get(id).callerDisplayNamePresentation);
454        }
455    }
456
457    public void sendSetConferenceableConnections(String id) throws Exception {
458        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
459            a.setConferenceableConnections(id, mConnectionById.get(id).conferenceableConnectionIds);
460        }
461    }
462
463    public void sendAddExistingConnection(String id) throws Exception {
464        for (IConnectionServiceAdapter a : mConnectionServiceAdapters) {
465            a.addExistingConnection(id, parcelable(mConnectionById.get(id)));
466        }
467    }
468
469    private ParcelableConference parcelable(ConferenceInfo c) {
470        return new ParcelableConference(
471                c.phoneAccount,
472                c.state,
473                c.capabilities,
474                c.connectionIds,
475                c.videoProvider,
476                c.videoState,
477                c.connectTimeMillis,
478                c.statusHints,
479                c.extras);
480    }
481
482    private ParcelableConnection parcelable(ConnectionInfo c) {
483        return new ParcelableConnection(
484                c.request.getAccountHandle(),
485                c.state,
486                c.capabilities,
487                c.request.getAddress(),
488                c.addressPresentation,
489                c.callerDisplayName,
490                c.callerDisplayNamePresentation,
491                c.videoProvider,
492                c.videoState,
493                false, /* ringback requested */
494                false, /* voip audio mode */
495                0, /* Connect Time for conf call on this connection */
496                c.statusHints,
497                c.disconnectCause,
498                c.conferenceableConnectionIds,
499                c.extras);
500    }
501}
502