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 android.content.Intent; 20 import android.telecom.Conference; 21 import android.telecom.Connection; 22 import android.telecom.ConnectionRequest; 23 import android.telecom.ConnectionService; 24 import android.telecom.PhoneAccountHandle; 25 import android.telecom.RemoteConference; 26 import android.telecom.RemoteConnection; 27 import android.util.Log; 28 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.concurrent.CountDownLatch; 32 33 /** 34 * This is the official ConnectionService for Telecom's CTS App. Since telecom requires that a 35 * CS be registered in the AndroidManifest.xml file, we have to have a single implementation 36 * of a CS and this is it. To test specific CS behavior, tests will implement their own CS and 37 * tell CtsConnectionService to forward any method invocations to that test's implementation. 38 * This is set up using {@link #setUp} and should be cleaned up before the end of the test using 39 * {@link #tearDown}. 40 * 41 * sConnectionService: Contains the connection service object provided by the current test in 42 * progress. We use this object to forward any communication received from the 43 * Telecom framework to the test connection service. 44 * sTelecomConnectionService: Contains the connection service object registered to the Telecom 45 * framework. We use this object to forward any communication from the 46 * test connection service to the Telecom framework. After Telecom 47 * binds to CtsConnectionService, this is set to be the instance of 48 * CtsConnectionService created by the framework after Telecom binds. 49 */ 50 public class CtsConnectionService extends ConnectionService { 51 private static String LOG_TAG = "CtsConnectionService"; 52 // This is the connection service implementation set locally during test setup. Telecom calls 53 // these overwritten methods. 54 private static ConnectionService sConnectionServiceTestImpl; 55 // Represents the connection from the test ConnectionService to telecom. Only valid once telecom 56 // has successfully bound. 57 private static ConnectionService sTelecomConnectionService; 58 private static CountDownLatch sTelecomUnboundLatch; 59 // Lock managing the setup and usage of sConnectionServiceTestImpl. 60 private static final Object sTestImplLock = new Object(); 61 // Lock managing the setup and usage of sTelecomConnectionService/sTelecomUnboundLatch. 62 private static final Object sTelecomCSLock = new Object(); 63 64 @Override onBindClient(Intent intent)65 public void onBindClient(Intent intent) { 66 Log.i("TelecomCTS", "CS bound"); 67 onTelecomConnected(this); 68 } 69 70 /** 71 * Call when a test is being setup to prepare this instance for testing. 72 * @param connectionService The ConnectionService impl to use to proxy commands to from telecom. 73 * @throws Exception There was an illegal state that caused the setup to fail. 74 */ setUp(ConnectionService connectionService)75 public static void setUp(ConnectionService connectionService) throws Exception { 76 setTestImpl(connectionService); 77 } 78 79 /** 80 * Call when a test is complete to tear down. 81 */ tearDown()82 public static void tearDown() { 83 clearTestImpl(); 84 } 85 86 @Override onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)87 public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, 88 ConnectionRequest request) { 89 ConnectionService testImpl = getTestImpl(); 90 if (testImpl != null) { 91 return testImpl.onCreateOutgoingConnection(connectionManagerPhoneAccount, request); 92 } else { 93 Log.e(LOG_TAG, 94 "Tried to create outgoing connection when sConnectionService null!"); 95 return null; 96 } 97 } 98 99 @Override onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)100 public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, 101 ConnectionRequest request) { 102 ConnectionService testImpl = getTestImpl(); 103 if (testImpl != null) { 104 return testImpl.onCreateIncomingConnection(connectionManagerPhoneAccount, request); 105 } else { 106 Log.e(LOG_TAG, 107 "Tried to create incoming connection when sConnectionService null!"); 108 return null; 109 } 110 } 111 112 @Override onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)113 public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 114 ConnectionRequest request) { 115 ConnectionService testImpl = getTestImpl(); 116 if (testImpl != null) { 117 testImpl.onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request); 118 } else { 119 Log.e(LOG_TAG, "onCreateIncomingConnectionFailed called when " 120 + "sConnectionService null!"); 121 } 122 } 123 124 @Override onCreateOutgoingConference(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)125 public Conference onCreateOutgoingConference(PhoneAccountHandle connectionManagerPhoneAccount, 126 ConnectionRequest request) { 127 ConnectionService testImpl = getTestImpl(); 128 if (testImpl != null) { 129 return testImpl.onCreateOutgoingConference(connectionManagerPhoneAccount, request); 130 } else { 131 Log.e(LOG_TAG, 132 "onCreateOutgoingConference called when sConnectionService null!"); 133 return null; 134 } 135 } 136 137 @Override onCreateOutgoingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)138 public void onCreateOutgoingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, 139 ConnectionRequest request) { 140 ConnectionService testImpl = getTestImpl(); 141 if (testImpl != null) { 142 testImpl.onCreateOutgoingConferenceFailed(connectionManagerPhoneAccount, request); 143 } else { 144 Log.e(LOG_TAG, 145 "onCreateOutgoingConferenceFailed called when sConnectionService null!"); 146 } 147 } 148 149 @Override onCreateIncomingConference(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)150 public Conference onCreateIncomingConference(PhoneAccountHandle connectionManagerPhoneAccount, 151 ConnectionRequest request) { 152 ConnectionService testImpl = getTestImpl(); 153 if (testImpl != null) { 154 return testImpl.onCreateIncomingConference(connectionManagerPhoneAccount, request); 155 } else { 156 Log.e(LOG_TAG, 157 "onCreateIncomingConference called when sConnectionService null!"); 158 return null; 159 } 160 } 161 162 @Override onCreateConnectionComplete(Connection connection)163 public void onCreateConnectionComplete(Connection connection) { 164 ConnectionService testImpl = getTestImpl(); 165 if (testImpl != null) { 166 testImpl.onCreateConnectionComplete(connection); 167 } else { 168 Log.e(LOG_TAG, "onCreateConnectionComplete called when " 169 + "sConnectionService null!"); 170 } 171 } 172 173 @Override onCreateConferenceComplete(Conference conference)174 public void onCreateConferenceComplete(Conference conference) { 175 ConnectionService testImpl = getTestImpl(); 176 if (testImpl != null) { 177 testImpl.onCreateConferenceComplete(conference); 178 } else { 179 Log.e(LOG_TAG, "onCreateConferenceComplete called when " 180 + "sConnectionService null!"); 181 } 182 } 183 184 @Override onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)185 public void onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, 186 ConnectionRequest request) { 187 ConnectionService testImpl = getTestImpl(); 188 if (testImpl != null) { 189 testImpl.onCreateIncomingConferenceFailed(connectionManagerPhoneAccount, request); 190 } else { 191 Log.e(LOG_TAG, 192 "onCreateIncomingConferenceFailed called when sConnectionService null!"); 193 } 194 } 195 196 @Override onConference(Connection connection1, Connection connection2)197 public void onConference(Connection connection1, Connection connection2) { 198 ConnectionService testImpl = getTestImpl(); 199 if (testImpl != null) { 200 testImpl.onConference(connection1, connection2); 201 } else { 202 Log.e(LOG_TAG, 203 "onConference called when sConnectionService null!"); 204 } 205 } 206 207 @Override onRemoteExistingConnectionAdded(RemoteConnection connection)208 public void onRemoteExistingConnectionAdded(RemoteConnection connection) { 209 ConnectionService testImpl = getTestImpl(); 210 if (testImpl != null) { 211 testImpl.onRemoteExistingConnectionAdded(connection); 212 } else { 213 Log.e(LOG_TAG, 214 "onRemoteExistingConnectionAdded called when sConnectionService null!"); 215 } 216 } 217 addConferenceToTelecom(Conference conference)218 public static void addConferenceToTelecom(Conference conference) { 219 ConnectionService telecomConn = getTelecomConnection(); 220 if (telecomConn != null) { 221 telecomConn.addConference(conference); 222 } else { 223 Log.e(LOG_TAG, "addConferenceToTelecom called when" 224 + " sTelecomConnectionService null!"); 225 } 226 } 227 addExistingConnectionToTelecom( PhoneAccountHandle phoneAccountHandle, Connection connection)228 public static void addExistingConnectionToTelecom( 229 PhoneAccountHandle phoneAccountHandle, Connection connection) { 230 ConnectionService telecomConn = getTelecomConnection(); 231 if (telecomConn != null) { 232 telecomConn.addExistingConnection(phoneAccountHandle, connection); 233 } else { 234 Log.e(LOG_TAG, "addExistingConnectionToTelecom called when" 235 + " sTelecomConnectionService null!"); 236 } 237 } 238 getAllConnectionsFromTelecom()239 public static Collection<Connection> getAllConnectionsFromTelecom() { 240 ConnectionService telecomConn = getTelecomConnection(); 241 if (telecomConn == null) { 242 return Collections.emptyList(); 243 } 244 return telecomConn.getAllConnections(); 245 } 246 createRemoteOutgoingConnectionToTelecom( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)247 public static RemoteConnection createRemoteOutgoingConnectionToTelecom( 248 PhoneAccountHandle connectionManagerPhoneAccount, 249 ConnectionRequest request) { 250 ConnectionService telecomConn = getTelecomConnection(); 251 if (telecomConn != null) { 252 return telecomConn.createRemoteOutgoingConnection( 253 connectionManagerPhoneAccount, request); 254 } else { 255 Log.e(LOG_TAG, "createRemoteOutgoingConnectionToTelecom called when" 256 + " sTelecomConnectionService null!"); 257 return null; 258 } 259 } 260 createRemoteIncomingConnectionToTelecom( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)261 public static RemoteConnection createRemoteIncomingConnectionToTelecom( 262 PhoneAccountHandle connectionManagerPhoneAccount, 263 ConnectionRequest request) { 264 ConnectionService telecomConn = getTelecomConnection(); 265 if (telecomConn != null) { 266 return telecomConn.createRemoteIncomingConnection( 267 connectionManagerPhoneAccount, request); 268 } else { 269 Log.e(LOG_TAG, "createRemoteIncomingConnectionToTelecom called when" 270 + " sTelecomConnectionService null!"); 271 return null; 272 } 273 } 274 createRemoteIncomingConferenceToTelecom( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)275 public static RemoteConference createRemoteIncomingConferenceToTelecom( 276 PhoneAccountHandle connectionManagerPhoneAccount, 277 ConnectionRequest request) { 278 ConnectionService telecomConn = getTelecomConnection(); 279 if (telecomConn != null) { 280 return telecomConn.createRemoteIncomingConference( 281 connectionManagerPhoneAccount, request); 282 } else { 283 Log.e(LOG_TAG, "createRemoteIncomingConferenceToTelecom called when" 284 + " sTelecomConnectionService null!"); 285 return null; 286 } 287 } 288 289 createRemoteOutgoingConferenceToTelecom( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)290 public static RemoteConference createRemoteOutgoingConferenceToTelecom( 291 PhoneAccountHandle connectionManagerPhoneAccount, 292 ConnectionRequest request) { 293 ConnectionService telecomConn = getTelecomConnection(); 294 if (telecomConn != null) { 295 return telecomConn.createRemoteOutgoingConference( 296 connectionManagerPhoneAccount, request); 297 } else { 298 Log.e(LOG_TAG, "createRemoteOutgoingConferenceToTelecom called when" 299 + " sTelecomConnectionService null!"); 300 return null; 301 } 302 } 303 304 @Override onRemoteConferenceAdded(RemoteConference conference)305 public void onRemoteConferenceAdded(RemoteConference conference) { 306 ConnectionService telecomConn = getTestImpl(); 307 if (telecomConn != null) { 308 telecomConn.onRemoteConferenceAdded(conference); 309 } else { 310 Log.e(LOG_TAG, "onRemoteConferenceAdded called when sConnectionService null!"); 311 } 312 } 313 314 @Override onConnectionServiceFocusGained()315 public void onConnectionServiceFocusGained() { 316 ConnectionService telecomConn = getTestImpl(); 317 if (telecomConn != null) { 318 telecomConn.onConnectionServiceFocusGained(); 319 } else { 320 Log.e(LOG_TAG, "onConnectionServiceFocusGained called when sConnectionService null!"); 321 } 322 } 323 324 @Override onConnectionServiceFocusLost()325 public void onConnectionServiceFocusLost() { 326 ConnectionService telecomConn = getTestImpl(); 327 if (telecomConn != null) { 328 telecomConn.onConnectionServiceFocusLost(); 329 } else { 330 Log.e(LOG_TAG, "onConnectionServiceFocusLost called when sConnectionService null!"); 331 } 332 } 333 334 @Override onUnbind(Intent intent)335 public boolean onUnbind(Intent intent) { 336 Log.i(LOG_TAG, "Service has been unbound"); 337 onTelecomDisconnected(); 338 return super.onUnbind(intent); 339 } 340 isServiceRegisteredToTelecom()341 public static boolean isServiceRegisteredToTelecom() { 342 return getTelecomConnection() != null; 343 } 344 345 /** 346 * @return Wait up to 5 seconds for the ConnectionService to be unbound from telecom. Return 347 * true if unbinding occurred successfully, false if it did not. 348 */ waitForUnBinding()349 public static boolean waitForUnBinding() { 350 CountDownLatch latch = getUnboundLatch(); 351 return TestUtils.waitForLatchCountDown(latch); 352 } 353 354 /** 355 * Setup the static connection to the ConnectionService that is handling commands to Telecom 356 * through ConnectionService. 357 */ onTelecomConnected(ConnectionService service)358 private static void onTelecomConnected(ConnectionService service) { 359 synchronized (sTelecomCSLock) { 360 sTelecomConnectionService = service; 361 if (sTelecomUnboundLatch != null && sTelecomUnboundLatch.getCount() > 0) { 362 Log.w(LOG_TAG, "Unexpected: Unbound latch has not counted down from previous " 363 + "usage"); 364 } 365 sTelecomUnboundLatch = new CountDownLatch(1); 366 } 367 } 368 369 /** 370 * Teardown a previously connected ConnectionService when Telecom unbinds. 371 */ onTelecomDisconnected()372 private static void onTelecomDisconnected() { 373 synchronized (sTelecomCSLock) { 374 if (sTelecomUnboundLatch != null) { 375 sTelecomUnboundLatch.countDown(); 376 } else { 377 Log.w(LOG_TAG, "Unexpected: null unbind latch, onBindClient never called."); 378 } 379 sTelecomConnectionService = null; 380 } 381 } 382 383 /** 384 * @return The ConnectionService that Telecom is connected through right now. This 385 * ConnectionService must only be used to communicate back to Telecom. This must be called to 386 * get the ConnectionService instance in order to keep synchronization across threads. 387 */ getTelecomConnection()388 private static ConnectionService getTelecomConnection() { 389 synchronized (sTelecomCSLock) { 390 return sTelecomConnectionService; 391 } 392 } 393 394 /** 395 * @return The CountDownLatch tracking when the ConnectionService will be unbound by Telecom. 396 */ getUnboundLatch()397 private static CountDownLatch getUnboundLatch() { 398 synchronized (sTelecomCSLock) { 399 return sTelecomUnboundLatch; 400 } 401 } 402 403 /** 404 * @return The ConnectionService that the test impl has provided to implement stub methods. 405 * This must be called to get the ConnectionService instance in order to keep synchronization 406 * across threads. 407 */ getTestImpl()408 private static ConnectionService getTestImpl() { 409 synchronized (sTestImplLock) { 410 return sConnectionServiceTestImpl; 411 } 412 } 413 414 /** 415 * Clear the ConnectionService when the test is tearing down. 416 */ clearTestImpl()417 private static void clearTestImpl() { 418 synchronized (sTestImplLock) { 419 sConnectionServiceTestImpl = null; 420 } 421 } 422 423 /** 424 * Sets the Implementation of ConnectionService stub methods for this test. Must be called 425 * before using any ConnectionService based tests. 426 * @param impl The ConnectionService instance that implements ConnectionService stub methods 427 * @throws Exception Invalid state occurred 428 */ setTestImpl(ConnectionService impl)429 private static void setTestImpl(ConnectionService impl) throws Exception { 430 synchronized (sTestImplLock) { 431 if (sConnectionServiceTestImpl != null) { 432 // Clean up so following tests don't fail too, hiding the original culprit in noise 433 sConnectionServiceTestImpl = null; 434 throw new Exception("Mock ConnectionService exists. Failed to call setUp(), " 435 + "or previous test failed to call tearDown()."); 436 } 437 sConnectionServiceTestImpl = impl; 438 } 439 } 440 } 441