1 /*
2  * Copyright (C) 2017 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 android.content.Intent;
20 import android.os.Bundle;
21 import android.telecom.Connection;
22 import android.telecom.ConnectionRequest;
23 import android.telecom.ConnectionService;
24 import android.telecom.DisconnectCause;
25 import android.telecom.PhoneAccountHandle;
26 import android.telecom.TelecomManager;
27 
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31 import java.util.concurrent.CountDownLatch;
32 
33 /**
34  * CTS test self-managed {@link ConnectionService} implementation.
35  */
36 public class CtsSelfManagedConnectionService extends ConnectionService {
37     // Constants used to address into the mLocks array.
38     public static int CONNECTION_CREATED_LOCK = 0;
39     public static int CREATE_INCOMING_CONNECTION_FAILED_LOCK = 1;
40     public static int CREATE_OUTGOING_CONNECTION_FAILED_LOCK = 2;
41     public static int HANDOVER_FAILED_LOCK = 3;
42     public static int FOCUS_GAINED_LOCK = 4;
43     public static int FOCUS_LOST_LOCK = 5;
44 
45     private static int NUM_LOCKS = FOCUS_LOST_LOCK + 1;
46 
47     private static CtsSelfManagedConnectionService sConnectionService;
48 
49     // Lock used to determine when binding to CS is complete.
50     private static CountDownLatch sBindingLock = new CountDownLatch(1);
51 
52     private SelfManagedConnection.Listener mConnectionListener =
53             new SelfManagedConnection.Listener() {
54         @Override
55         void onDestroyed(SelfManagedConnection connection) {
56             mConnections.remove(connection);
57         }
58     };
59 
60     private CountDownLatch[] mLocks = new CountDownLatch[NUM_LOCKS];
61 
62     private Object mLock = new Object();
63     private List<SelfManagedConnection> mConnections = new ArrayList<>();
64     private TestUtils.InvokeCounter mOnCreateIncomingHandoverConnectionCounter =
65             new TestUtils.InvokeCounter("incomingHandoverConnection");
66     private TestUtils.InvokeCounter mOnCreateOutgoingHandoverConnectionCounter =
67             new TestUtils.InvokeCounter("outgoingHandoverConnection");
68 
getConnectionService()69     public static CtsSelfManagedConnectionService getConnectionService() {
70         return sConnectionService;
71     }
72 
CtsSelfManagedConnectionService()73     public CtsSelfManagedConnectionService() throws Exception {
74         super();
75         sConnectionService = this;
76         Arrays.setAll(mLocks, i -> new CountDownLatch(1));
77 
78         // Inform anyone waiting on binding that we're bound.
79         sBindingLock.countDown();
80     }
81 
82     @Override
onUnbind(Intent intent)83     public boolean onUnbind(Intent intent) {
84         sBindingLock = new CountDownLatch(1);
85         return super.onUnbind(intent);
86     }
87 
88     @Override
onCreateOutgoingConnection(PhoneAccountHandle connectionManagerAccount, final ConnectionRequest request)89     public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerAccount,
90             final ConnectionRequest request) {
91 
92         return createSelfManagedConnection(request, false);
93     }
94 
95     @Override
onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)96     public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
97             ConnectionRequest request) {
98         return createSelfManagedConnection(request, true);
99     }
100 
101     @Override
onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerHandle, ConnectionRequest request)102     public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerHandle,
103                                                  ConnectionRequest request) {
104         mLocks[CREATE_INCOMING_CONNECTION_FAILED_LOCK].countDown();
105     }
106 
107     @Override
onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerHandle, ConnectionRequest request)108     public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerHandle,
109                                                  ConnectionRequest request) {
110         mLocks[CREATE_OUTGOING_CONNECTION_FAILED_LOCK].countDown();
111     }
112 
113     @Override
onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)114     public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
115             ConnectionRequest request) {
116         mOnCreateIncomingHandoverConnectionCounter.invoke(fromPhoneAccountHandle, request);
117         return createSelfManagedConnection(request, true /* incoming */);
118     }
119 
120     @Override
onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)121     public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
122             ConnectionRequest request) {
123         mOnCreateOutgoingHandoverConnectionCounter.invoke(fromPhoneAccountHandle, request);
124         return createSelfManagedConnection(request, false /* incoming */);
125     }
126 
127     @Override
onHandoverFailed(ConnectionRequest request, int error)128     public void onHandoverFailed(ConnectionRequest request, int error) {
129         mLocks[HANDOVER_FAILED_LOCK].countDown();
130     }
131 
132 
133     @Override
onConnectionServiceFocusGained()134     public void onConnectionServiceFocusGained() {
135         mLocks[FOCUS_GAINED_LOCK].countDown();
136     }
137 
138     @Override
onConnectionServiceFocusLost()139     public void onConnectionServiceFocusLost() {
140         mLocks[FOCUS_LOST_LOCK].countDown();
141         connectionServiceFocusReleased();
142     }
143 
tearDown()144     public void tearDown() {
145         synchronized(mLock) {
146             if (mConnections != null && mConnections.size() > 0) {
147                 mConnections.forEach(connection -> {
148                             connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
149                             connection.destroy();
150                         }
151                 );
152                 mConnections.clear();
153             }
154         }
155         sBindingLock = new CountDownLatch(1);
156     }
157 
createSelfManagedConnection(ConnectionRequest request, boolean isIncoming)158     private Connection createSelfManagedConnection(ConnectionRequest request, boolean isIncoming) {
159         SelfManagedConnection connection = new SelfManagedConnection(isIncoming,
160                 mConnectionListener);
161         connection.setConnectionProperties(Connection.PROPERTY_SELF_MANAGED);
162         connection.setConnectionCapabilities(
163                 Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD);
164         connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
165         connection.setExtras(request.getExtras());
166 
167         Bundle moreExtras = new Bundle();
168         moreExtras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
169                 request.getAccountHandle());
170         connection.putExtras(moreExtras);
171         connection.setVideoState(request.getVideoState());
172 
173         if (isIncoming) {
174             connection.setRinging();
175         } else {
176             connection.setInitializing();
177         }
178 
179         synchronized(mLock) {
180             mConnections.add(connection);
181         }
182         mLocks[CONNECTION_CREATED_LOCK].countDown();
183         return connection;
184     }
185 
getConnections()186     public List<SelfManagedConnection> getConnections() {
187         synchronized(mLock) {
188             return new ArrayList<>(mConnections);
189         }
190     }
191 
192     /**
193      * Waits on a lock for maximum of 5 seconds.
194      *
195      * @param lock one of the {@code *_LOCK} constants defined above.
196      * @return {@code true} if the lock was released within the time limit, {@code false} if the
197      *      timeout expired without the lock being released.
198      */
waitForUpdate(int lock)199     public boolean waitForUpdate(int lock) {
200         mLocks[lock] = TestUtils.waitForLock(mLocks[lock]);
201         return mLocks[lock] != null;
202     }
203 
204     /**
205      * Waits for the {@link ConnectionService} to be found.
206      * @return {@code true} if binding happened within the time limit, or {@code false} otherwise.
207      */
waitForBinding()208     public static boolean waitForBinding() {
209         return TestUtils.waitForLatchCountDown(sBindingLock);
210     }
211 
getOnCreateIncomingHandoverConnectionCounter()212     public TestUtils.InvokeCounter getOnCreateIncomingHandoverConnectionCounter() {
213         return mOnCreateIncomingHandoverConnectionCounter;
214     }
215 
getOnCreateOutgoingHandoverConnectionCounter()216     public TestUtils.InvokeCounter getOnCreateOutgoingHandoverConnectionCounter() {
217         return mOnCreateOutgoingHandoverConnectionCounter;
218     }
219 }
220