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