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 android.telecom.cts;
18 
19 import static android.telecom.cts.ThirdPartyCallScreeningServiceTest.EXTRA_NETWORK_IDENTIFIED_EMERGENCY_CALL;
20 
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.telecom.Conference;
24 import android.telecom.Connection;
25 import android.telecom.ConnectionRequest;
26 import android.telecom.ConnectionService;
27 import android.telecom.DisconnectCause;
28 import android.telecom.PhoneAccountHandle;
29 import android.telecom.RemoteConference;
30 import android.telecom.RemoteConnection;
31 import android.telecom.TelecomManager;
32 
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.concurrent.Semaphore;
36 import java.util.concurrent.TimeUnit;
37 
38 /**
39  * Default implementation of a {@link CtsConnectionService}. This is used for the majority
40  * of Telecom CTS tests that simply require that a outgoing call is placed, or incoming call is
41  * received.
42  */
43 public class MockConnectionService extends ConnectionService {
44     public static final Uri VERSTAT_NOT_VERIFIED_NUMBER = Uri.fromParts("tel", "777", null);
45     public static final Uri VERSTAT_PASSED_NUMBER = Uri.fromParts("tel", "555", null);
46     public static final Uri VERSTAT_FAILED_NUMBER = Uri.fromParts("tel", "333", null);
47     public static final String EXTRA_TEST = "com.android.telecom.extra.TEST";
48     public static final String TEST_VALUE = "we've got it";
49     public static final int CONNECTION_PRESENTATION =  TelecomManager.PRESENTATION_ALLOWED;
50 
51     public static final int EVENT_CONNECTION_SERVICE_FOCUS_GAINED = 0;
52     public static final int EVENT_CONNECTION_SERVICE_FOCUS_LOST = 1;
53     public static final int EVENT_CONNECTION_SERVICE_CREATE_CONNECTION = 2;
54     public static final int EVENT_CONNECTION_SERVICE_CREATE_CONNECTION_FAILED = 3;
55     public static final int EVENT_CONNECTION_SERVICE_CREATE_CONNECTION_COMPLETE = 4;
56     public static final int EVENT_CONNECTION_SERVICE_CREATE_CONFERENCE_COMPLETE = 5;
57     // Update TOTAL_EVENT below with last event.
58     private static final int TOTAL_EVENT = EVENT_CONNECTION_SERVICE_CREATE_CONFERENCE_COMPLETE + 1;
59 
60     private static final int DEFAULT_EVENT_TIMEOUT_MS = 2000;
61 
62     private final Semaphore[] mEventLock = initializeSemaphore(TOTAL_EVENT);
63 
64     /**
65      * Used to control whether the {@link MockVideoProvider} will be created when connections are
66      * created.  Used by {@link VideoCallTest#testVideoCallDelayProvider()} to test scenario where
67      * the {@link MockVideoProvider} is not created immediately when the Connection is created.
68      */
69     private boolean mCreateVideoProvider = true;
70 
71     public Semaphore lock = new Semaphore(0);
72     public List<MockConnection> outgoingConnections = new ArrayList<MockConnection>();
73     public List<MockConnection> incomingConnections = new ArrayList<MockConnection>();
74     public List<RemoteConnection> remoteConnections = new ArrayList<RemoteConnection>();
75     public List<MockConference> conferences = new ArrayList<MockConference>();
76     public List<RemoteConference> remoteConferences = new ArrayList<RemoteConference>();
77     public List<MockConnection> failedConnections = new ArrayList<>();
78     public ConnectionRequest connectionRequest = null;
79 
80     @Override
onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)81     public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
82             ConnectionRequest request) {
83         final MockConnection connection = new MockConnection();
84         connection.setAddress(request.getAddress(), CONNECTION_PRESENTATION);
85         connection.setMockPhoneAccountHandle(connectionManagerPhoneAccount);
86         connection.setConnectionCapabilities(Connection.CAPABILITY_SUPPORT_HOLD |
87                 Connection.CAPABILITY_HOLD
88                 | Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL
89                 | Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
90         if (mCreateVideoProvider) {
91             connection.createMockVideoProvider();
92         } else {
93             mCreateVideoProvider = true;
94         }
95         connection.setVideoState(request.getVideoState());
96         connection.setInitializing();
97         if (request.isRequestingRtt()) {
98             connection.setRttTextStream(request.getRttTextStream());
99             connection.setConnectionProperties(connection.getConnectionProperties() |
100                     Connection.PROPERTY_IS_RTT);
101         }
102         Bundle testExtra;
103         if (request.getExtras() != null) {
104             testExtra = request.getExtras();
105         } else {
106             testExtra = new Bundle();
107         }
108         // Emit an extra into the connection.  We'll see if it makes it through.
109         testExtra.putString(EXTRA_TEST, TEST_VALUE);
110         connection.putExtras(testExtra);
111         outgoingConnections.add(connection);
112         lock.release();
113         mEventLock[EVENT_CONNECTION_SERVICE_CREATE_CONNECTION].release();
114         return connection;
115     }
116 
117     @Override
onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)118     public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
119             ConnectionRequest request) {
120         final MockConnection connection = new MockConnection();
121         connection.setAddress(request.getAddress(), CONNECTION_PRESENTATION);
122         connection.setConnectionCapabilities(connection.getConnectionCapabilities()
123                 | Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION
124                 | Connection.CAPABILITY_SUPPORT_HOLD
125                 | Connection.CAPABILITY_HOLD);
126         connection.createMockVideoProvider();
127         ((Connection) connection).setVideoState(request.getVideoState());
128         if (request.isRequestingRtt()) {
129             connection.setRttTextStream(request.getRttTextStream());
130             connection.setConnectionProperties(connection.getConnectionProperties() |
131                     Connection.PROPERTY_IS_RTT);
132         }
133         connection.setRinging();
134         // Emit an extra into the connection.  We'll see if it makes it through.
135         Bundle testExtra = new Bundle();
136         testExtra.putString(EXTRA_TEST, TEST_VALUE);
137         connection.putExtras(testExtra);
138         if (VERSTAT_NOT_VERIFIED_NUMBER.equals(request.getAddress())) {
139             connection.setCallerNumberVerificationStatus(
140                     Connection.VERIFICATION_STATUS_NOT_VERIFIED);
141         } else if (VERSTAT_PASSED_NUMBER.equals(request.getAddress())) {
142             connection.setCallerNumberVerificationStatus(
143                     Connection.VERIFICATION_STATUS_PASSED);
144         } else if (VERSTAT_FAILED_NUMBER.equals(request.getAddress())) {
145             connection.setCallerNumberVerificationStatus(
146                     Connection.VERIFICATION_STATUS_FAILED);
147         }
148 
149         Bundle requestExtra = request.getExtras();
150         if (requestExtra.getBoolean(EXTRA_NETWORK_IDENTIFIED_EMERGENCY_CALL, false)) {
151             connection.setConnectionProperties(
152                     Connection.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL);
153         }
154         incomingConnections.add(connection);
155         lock.release();
156         mEventLock[EVENT_CONNECTION_SERVICE_CREATE_CONNECTION].release();
157         return connection;
158     }
159 
160     @Override
onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)161     public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
162             ConnectionRequest request) {
163         final MockConnection connection = new MockConnection();
164         connection.setAddress(request.getAddress(), CONNECTION_PRESENTATION);
165         connection.setPhoneAccountHandle(connectionManagerPhoneAccount);
166         failedConnections.add(connection);
167         lock.release();
168         mEventLock[EVENT_CONNECTION_SERVICE_CREATE_CONNECTION_FAILED].release();
169     }
170 
171     @Override
onConference(Connection connection1, Connection connection2)172     public void onConference(Connection connection1, Connection connection2) {
173         // Make sure that these connections are already not conferenced.
174         if (connection1.getConference() == null && connection2.getConference() == null) {
175             MockConference conference = new MockConference(
176                     (MockConnection)connection1, (MockConnection)connection2);
177             CtsConnectionService.addConferenceToTelecom(conference);
178             conferences.add(conference);
179 
180             if (connection1.getState() == Connection.STATE_HOLDING){
181                 connection1.setActive();
182             }
183             if(connection2.getState() == Connection.STATE_HOLDING){
184                 connection2.setActive();
185             }
186 
187             lock.release();
188         }
189     }
190     @Override
onCreateOutgoingConference(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)191     public Conference onCreateOutgoingConference(PhoneAccountHandle connectionManagerPhoneAccount,
192             ConnectionRequest request) {
193         final Connection connection = onCreateOutgoingConnection(connectionManagerPhoneAccount,
194                 request);
195         final MockConference conference = new MockConference(connectionManagerPhoneAccount);
196         conference.addConnection(connection);
197         conferences.add(conference);
198         connectionRequest = request;
199         lock.release();
200         return conference;
201     }
202 
203     @Override
onCreateOutgoingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)204     public void onCreateOutgoingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount,
205             ConnectionRequest request) {
206         final MockConference conference = new MockConference(connectionManagerPhoneAccount);
207         conference.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
208         conferences.add(conference);
209         connectionRequest = request;
210         lock.release(2);
211     }
212 
213     @Override
onCreateIncomingConference(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)214     public Conference onCreateIncomingConference(PhoneAccountHandle connectionManagerPhoneAccount,
215             ConnectionRequest request) {
216         final Connection connection = onCreateIncomingConnection(connectionManagerPhoneAccount,
217                 request);
218         final MockConference conference = new MockConference(connectionManagerPhoneAccount);
219         conference.addConnection(connection);
220         connectionRequest = request;
221         conferences.add(conference);
222         lock.release();
223         return conference;
224     }
225 
226     @Override
onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)227     public void onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount,
228             ConnectionRequest request) {
229         final MockConference conference = new MockConference(connectionManagerPhoneAccount);
230         conference.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
231         conferences.add(conference);
232         connectionRequest = request;
233         lock.release(2);
234     }
235 
236     @Override
onCreateConnectionComplete(Connection connection)237     public void onCreateConnectionComplete(Connection connection) {
238         mEventLock[EVENT_CONNECTION_SERVICE_CREATE_CONNECTION_COMPLETE].release();
239     }
240 
241     @Override
onCreateConferenceComplete(Conference conference)242     public void onCreateConferenceComplete(Conference conference) {
243         mEventLock[EVENT_CONNECTION_SERVICE_CREATE_CONFERENCE_COMPLETE].release();
244     }
245 
246     @Override
onRemoteExistingConnectionAdded(RemoteConnection connection)247     public void onRemoteExistingConnectionAdded(RemoteConnection connection) {
248         // Keep track of the remote connections added to the service
249         remoteConnections.add(connection);
250     }
251 
252     @Override
onRemoteConferenceAdded(RemoteConference conference)253     public void onRemoteConferenceAdded(RemoteConference conference) {
254         // Keep track of the remote connections added to the service
255         remoteConferences.add(conference);
256     }
257 
258     @Override
onConnectionServiceFocusGained()259     public void onConnectionServiceFocusGained() {
260         mEventLock[EVENT_CONNECTION_SERVICE_FOCUS_GAINED].release();
261     }
262 
263     @Override
onConnectionServiceFocusLost()264     public void onConnectionServiceFocusLost() {
265         mEventLock[EVENT_CONNECTION_SERVICE_FOCUS_LOST].release();
266         connectionServiceFocusReleased();
267     }
268 
setCreateVideoProvider(boolean createVideoProvider)269     public void setCreateVideoProvider(boolean createVideoProvider) {
270         mCreateVideoProvider = createVideoProvider;
271     }
272 
273     /** Returns true if the given {@code event} is happened before the default timeout. */
waitForEvent(int event)274     public boolean waitForEvent(int event) {
275         if (event < 0 || event >= mEventLock.length) {
276             return false;
277         }
278 
279         try {
280             return mEventLock[event].tryAcquire(DEFAULT_EVENT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
281         } catch (InterruptedException e) {
282             // No interaction for the given event within the given timeout.
283             return false;
284         }
285     }
286 
initializeSemaphore(int total)287     private static final Semaphore[] initializeSemaphore(int total) {
288         Semaphore[] locks = new Semaphore[total];
289         for (int i = 0; i < total; i++) {
290             locks[i] = new Semaphore(0);
291         }
292         return locks;
293     }
294 }
295