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