ConnectionServiceFixture.java revision 953e1af643b66df6f931d76c23bcc54147668cd4
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 17package com.android.server.telecom.tests; 18 19import com.android.internal.telecom.IConnectionService; 20import com.android.internal.telecom.IConnectionServiceAdapter; 21import com.android.internal.telecom.IVideoProvider; 22import com.android.internal.telecom.RemoteServiceCallback; 23 24import junit.framework.TestCase; 25 26import org.mockito.Mockito; 27 28import android.content.ComponentName; 29import android.net.Uri; 30import android.os.Bundle; 31import android.os.IBinder; 32import android.os.IInterface; 33import android.os.RemoteException; 34import android.telecom.CallAudioState; 35import android.telecom.Conference; 36import android.telecom.Connection; 37import android.telecom.ConnectionRequest; 38import android.telecom.ConnectionService; 39import android.telecom.DisconnectCause; 40import android.telecom.Log; 41import android.telecom.ParcelableConference; 42import android.telecom.ParcelableConnection; 43import android.telecom.PhoneAccountHandle; 44import android.telecom.StatusHints; 45import android.telecom.TelecomManager; 46 47import com.google.android.collect.Lists; 48 49import java.lang.Override; 50import java.util.ArrayList; 51import java.util.Collection; 52import java.util.HashMap; 53import java.util.HashSet; 54import java.util.List; 55import java.util.Map; 56import java.util.Set; 57import java.util.concurrent.CountDownLatch; 58import java.util.concurrent.TimeUnit; 59 60/** 61 * Controls a test {@link IConnectionService} as would be provided by a source of connectivity 62 * to the Telecom framework. 63 */ 64public class ConnectionServiceFixture implements TestFixture<IConnectionService> { 65 static int INVALID_VIDEO_STATE = -1; 66 public CountDownLatch mExtrasLock = new CountDownLatch(1); 67 static int NOT_SPECIFIED = 0; 68 69 /** 70 * Implementation of ConnectionService that performs no-ops for tasks normally meant for 71 * Telephony and reports success back to Telecom 72 */ 73 public class FakeConnectionServiceDelegate extends ConnectionService { 74 int mVideoState = INVALID_VIDEO_STATE; 75 int mCapabilities = NOT_SPECIFIED; 76 int mProperties = NOT_SPECIFIED; 77 78 @Override 79 public Connection onCreateUnknownConnection( 80 PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) { 81 mLatestConnection = new FakeConnection(request.getVideoState(), request.getAddress()); 82 return mLatestConnection; 83 } 84 85 @Override 86 public Connection onCreateIncomingConnection( 87 PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) { 88 FakeConnection fakeConnection = new FakeConnection( 89 mVideoState == INVALID_VIDEO_STATE ? request.getVideoState() : mVideoState, 90 request.getAddress()); 91 mLatestConnection = fakeConnection; 92 if (mCapabilities != NOT_SPECIFIED) { 93 fakeConnection.setConnectionCapabilities(mCapabilities); 94 } 95 if (mProperties != NOT_SPECIFIED) { 96 fakeConnection.setConnectionProperties(mProperties); 97 } 98 99 return fakeConnection; 100 } 101 102 @Override 103 public Connection onCreateOutgoingConnection( 104 PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) { 105 mLatestConnection = new FakeConnection(request.getVideoState(), request.getAddress()); 106 return mLatestConnection; 107 } 108 109 @Override 110 public void onConference(Connection cxn1, Connection cxn2) { 111 // Usually, this is implemented by something in Telephony, which does a bunch of radio 112 // work to conference the two connections together. Here we just short-cut that and 113 // declare them conferenced. 114 Conference fakeConference = new FakeConference(); 115 fakeConference.addConnection(cxn1); 116 fakeConference.addConnection(cxn2); 117 mLatestConference = fakeConference; 118 addConference(fakeConference); 119 } 120 } 121 122 public class FakeConnection extends Connection { 123 public FakeConnection(int videoState, Uri address) { 124 super(); 125 int capabilities = getConnectionCapabilities(); 126 capabilities |= CAPABILITY_MUTE; 127 capabilities |= CAPABILITY_SUPPORT_HOLD; 128 capabilities |= CAPABILITY_HOLD; 129 setVideoState(videoState); 130 setConnectionCapabilities(capabilities); 131 setActive(); 132 setAddress(address, TelecomManager.PRESENTATION_ALLOWED); 133 } 134 135 @Override 136 public void onExtrasChanged(Bundle extras) { 137 mExtrasLock.countDown(); 138 } 139 } 140 141 public class FakeConference extends Conference { 142 public FakeConference() { 143 super(null); 144 setConnectionCapabilities( 145 Connection.CAPABILITY_SUPPORT_HOLD 146 | Connection.CAPABILITY_HOLD 147 | Connection.CAPABILITY_MUTE 148 | Connection.CAPABILITY_MANAGE_CONFERENCE); 149 } 150 151 @Override 152 public void onMerge(Connection connection) { 153 // Do nothing besides inform the connection that it was merged into this conference. 154 connection.setConference(this); 155 } 156 157 @Override 158 public void onExtrasChanged(Bundle extras) { 159 Log.w(this, "FakeConference onExtrasChanged"); 160 mExtrasLock.countDown(); 161 } 162 } 163 164 public class FakeConnectionService extends IConnectionService.Stub { 165 List<String> rejectedCallIds = Lists.newArrayList(); 166 167 @Override 168 public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) 169 throws RemoteException { 170 if (!mConnectionServiceAdapters.add(adapter)) { 171 throw new RuntimeException("Adapter already added: " + adapter); 172 } 173 mConnectionServiceDelegateAdapter.addConnectionServiceAdapter(adapter); 174 } 175 176 @Override 177 public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) 178 throws RemoteException { 179 if (!mConnectionServiceAdapters.remove(adapter)) { 180 throw new RuntimeException("Adapter never added: " + adapter); 181 } 182 mConnectionServiceDelegateAdapter.removeConnectionServiceAdapter(adapter); 183 } 184 185 @Override 186 public void createConnection(PhoneAccountHandle connectionManagerPhoneAccount, 187 String id, 188 ConnectionRequest request, boolean isIncoming, boolean isUnknown) 189 throws RemoteException { 190 Log.i(ConnectionServiceFixture.this, "xoxox createConnection --> " + id); 191 192 if (mConnectionById.containsKey(id)) { 193 throw new RuntimeException("Connection already exists: " + id); 194 } 195 mLatestConnectionId = id; 196 ConnectionInfo c = new ConnectionInfo(); 197 c.connectionManagerPhoneAccount = connectionManagerPhoneAccount; 198 c.id = id; 199 c.request = request; 200 c.isIncoming = isIncoming; 201 c.isUnknown = isUnknown; 202 c.capabilities |= Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD; 203 c.videoState = request.getVideoState(); 204 c.mockVideoProvider = new MockVideoProvider(); 205 c.videoProvider = c.mockVideoProvider.getInterface(); 206 mConnectionById.put(id, c); 207 mConnectionServiceDelegateAdapter.createConnection(connectionManagerPhoneAccount, 208 id, request, isIncoming, isUnknown); 209 } 210 211 @Override 212 public void abort(String callId) throws RemoteException { } 213 214 @Override 215 public void answerVideo(String callId, int videoState) throws RemoteException { } 216 217 @Override 218 public void answer(String callId) throws RemoteException { } 219 220 @Override 221 public void reject(String callId) throws RemoteException { 222 rejectedCallIds.add(callId); 223 } 224 225 @Override 226 public void rejectWithMessage(String callId, String message) throws RemoteException { } 227 228 @Override 229 public void disconnect(String callId) throws RemoteException { } 230 231 @Override 232 public void silence(String callId) throws RemoteException { } 233 234 @Override 235 public void hold(String callId) throws RemoteException { } 236 237 @Override 238 public void unhold(String callId) throws RemoteException { } 239 240 @Override 241 public void onCallAudioStateChanged(String activeCallId, CallAudioState audioState) 242 throws RemoteException { } 243 244 @Override 245 public void playDtmfTone(String callId, char digit) throws RemoteException { } 246 247 @Override 248 public void stopDtmfTone(String callId) throws RemoteException { } 249 250 @Override 251 public void conference(String conferenceCallId, String callId) throws RemoteException { 252 mConnectionServiceDelegateAdapter.conference(conferenceCallId, callId); 253 } 254 255 @Override 256 public void splitFromConference(String callId) throws RemoteException { } 257 258 @Override 259 public void mergeConference(String conferenceCallId) throws RemoteException { } 260 261 @Override 262 public void swapConference(String conferenceCallId) throws RemoteException { } 263 264 @Override 265 public void onPostDialContinue(String callId, boolean proceed) throws RemoteException { } 266 267 @Override 268 public void pullExternalCall(String callId) throws RemoteException { } 269 270 @Override 271 public void sendCallEvent(String callId, String event, Bundle extras) throws RemoteException 272 {} 273 274 public void onExtrasChanged(String callId, Bundle extras) throws RemoteException { 275 mConnectionServiceDelegateAdapter.onExtrasChanged(callId, extras); 276 } 277 278 @Override 279 public IBinder asBinder() { 280 return this; 281 } 282 283 @Override 284 public IInterface queryLocalInterface(String descriptor) { 285 return this; 286 } 287 } 288 289 FakeConnectionServiceDelegate mConnectionServiceDelegate = 290 new FakeConnectionServiceDelegate(); 291 private IConnectionService mConnectionServiceDelegateAdapter = 292 IConnectionService.Stub.asInterface(mConnectionServiceDelegate.onBind(null)); 293 294 FakeConnectionService mConnectionService = new FakeConnectionService(); 295 private IConnectionService.Stub mConnectionServiceSpy = Mockito.spy(mConnectionService); 296 297 public class ConnectionInfo { 298 PhoneAccountHandle connectionManagerPhoneAccount; 299 String id; 300 boolean ringing; 301 ConnectionRequest request; 302 boolean isIncoming; 303 boolean isUnknown; 304 int state; 305 int addressPresentation; 306 int capabilities; 307 int properties; 308 StatusHints statusHints; 309 DisconnectCause disconnectCause; 310 String conferenceId; 311 String callerDisplayName; 312 int callerDisplayNamePresentation; 313 final List<String> conferenceableConnectionIds = new ArrayList<>(); 314 IVideoProvider videoProvider; 315 Connection.VideoProvider videoProviderImpl; 316 MockVideoProvider mockVideoProvider; 317 int videoState; 318 boolean isVoipAudioMode; 319 Bundle extras; 320 } 321 322 public class ConferenceInfo { 323 PhoneAccountHandle phoneAccount; 324 int state; 325 int capabilities; 326 int properties; 327 final List<String> connectionIds = new ArrayList<>(); 328 IVideoProvider videoProvider; 329 int videoState; 330 long connectTimeMillis; 331 StatusHints statusHints; 332 Bundle extras; 333 } 334 335 public String mLatestConnectionId; 336 public Connection mLatestConnection; 337 public Conference mLatestConference; 338 public final Set<IConnectionServiceAdapter> mConnectionServiceAdapters = new HashSet<>(); 339 public final Map<String, ConnectionInfo> mConnectionById = new HashMap<>(); 340 public final Map<String, ConferenceInfo> mConferenceById = new HashMap<>(); 341 public final List<ComponentName> mRemoteConnectionServiceNames = new ArrayList<>(); 342 public final List<IBinder> mRemoteConnectionServices = new ArrayList<>(); 343 344 public ConnectionServiceFixture() throws Exception { } 345 346 @Override 347 public IConnectionService getTestDouble() { 348 return mConnectionServiceSpy; 349 } 350 351 public void sendHandleCreateConnectionComplete(String id) throws Exception { 352 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 353 a.handleCreateConnectionComplete( 354 id, 355 mConnectionById.get(id).request, 356 parcelable(mConnectionById.get(id))); 357 } 358 } 359 360 public void sendSetActive(String id) throws Exception { 361 mConnectionById.get(id).state = Connection.STATE_ACTIVE; 362 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 363 a.setActive(id); 364 } 365 } 366 367 public void sendSetRinging(String id) throws Exception { 368 mConnectionById.get(id).state = Connection.STATE_RINGING; 369 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 370 a.setRinging(id); 371 } 372 } 373 374 public void sendSetDialing(String id) throws Exception { 375 mConnectionById.get(id).state = Connection.STATE_DIALING; 376 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 377 a.setDialing(id); 378 } 379 } 380 381 public void sendSetDisconnected(String id, int disconnectCause) throws Exception { 382 mConnectionById.get(id).state = Connection.STATE_DISCONNECTED; 383 mConnectionById.get(id).disconnectCause = new DisconnectCause(disconnectCause); 384 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 385 a.setDisconnected(id, mConnectionById.get(id).disconnectCause); 386 } 387 } 388 389 public void sendSetOnHold(String id) throws Exception { 390 mConnectionById.get(id).state = Connection.STATE_HOLDING; 391 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 392 a.setOnHold(id); 393 } 394 } 395 396 public void sendSetRingbackRequested(String id) throws Exception { 397 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 398 a.setRingbackRequested(id, mConnectionById.get(id).ringing); 399 } 400 } 401 402 public void sendSetConnectionCapabilities(String id) throws Exception { 403 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 404 a.setConnectionCapabilities(id, mConnectionById.get(id).capabilities); 405 } 406 } 407 408 public void sendSetIsConferenced(String id) throws Exception { 409 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 410 a.setIsConferenced(id, mConnectionById.get(id).conferenceId); 411 } 412 } 413 414 public void sendAddConferenceCall(String id) throws Exception { 415 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 416 a.addConferenceCall(id, parcelable(mConferenceById.get(id))); 417 } 418 } 419 420 public void sendRemoveCall(String id) throws Exception { 421 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 422 a.removeCall(id); 423 } 424 } 425 426 public void sendOnPostDialWait(String id, String remaining) throws Exception { 427 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 428 a.onPostDialWait(id, remaining); 429 } 430 } 431 432 public void sendOnPostDialChar(String id, char nextChar) throws Exception { 433 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 434 a.onPostDialChar(id, nextChar); 435 } 436 } 437 438 public void sendQueryRemoteConnectionServices() throws Exception { 439 mRemoteConnectionServices.clear(); 440 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 441 a.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() { 442 @Override 443 public void onError() throws RemoteException { 444 throw new RuntimeException(); 445 } 446 447 @Override 448 public void onResult( 449 List<ComponentName> names, 450 List<IBinder> services) 451 throws RemoteException { 452 TestCase.assertEquals(names.size(), services.size()); 453 mRemoteConnectionServiceNames.addAll(names); 454 mRemoteConnectionServices.addAll(services); 455 } 456 457 @Override 458 public IBinder asBinder() { 459 return this; 460 } 461 }); 462 } 463 } 464 465 public void sendSetVideoProvider(String id) throws Exception { 466 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 467 a.setVideoProvider(id, mConnectionById.get(id).videoProvider); 468 } 469 } 470 471 public void sendSetVideoState(String id) throws Exception { 472 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 473 a.setVideoState(id, mConnectionById.get(id).videoState); 474 } 475 } 476 477 public void sendSetIsVoipAudioMode(String id) throws Exception { 478 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 479 a.setIsVoipAudioMode(id, mConnectionById.get(id).isVoipAudioMode); 480 } 481 } 482 483 public void sendSetStatusHints(String id) throws Exception { 484 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 485 a.setStatusHints(id, mConnectionById.get(id).statusHints); 486 } 487 } 488 489 public void sendSetAddress(String id) throws Exception { 490 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 491 a.setAddress( 492 id, 493 mConnectionById.get(id).request.getAddress(), 494 mConnectionById.get(id).addressPresentation); 495 } 496 } 497 498 public void sendSetCallerDisplayName(String id) throws Exception { 499 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 500 a.setCallerDisplayName( 501 id, 502 mConnectionById.get(id).callerDisplayName, 503 mConnectionById.get(id).callerDisplayNamePresentation); 504 } 505 } 506 507 public void sendSetConferenceableConnections(String id) throws Exception { 508 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 509 a.setConferenceableConnections(id, mConnectionById.get(id).conferenceableConnectionIds); 510 } 511 } 512 513 public void sendAddExistingConnection(String id) throws Exception { 514 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 515 a.addExistingConnection(id, parcelable(mConnectionById.get(id))); 516 } 517 } 518 519 public void sendConnectionEvent(String id, String event, Bundle extras) throws Exception { 520 for (IConnectionServiceAdapter a : mConnectionServiceAdapters) { 521 a.onConnectionEvent(id, event, extras); 522 } 523 } 524 525 /** 526 * Waits until the {@link Connection#onExtrasChanged(Bundle)} API has been called on a 527 * {@link Connection} or {@link Conference}. 528 */ 529 public void waitForExtras() { 530 try { 531 mExtrasLock.await(TelecomSystemTest.TEST_TIMEOUT, TimeUnit.MILLISECONDS); 532 } catch (InterruptedException ie) { 533 } 534 mExtrasLock = new CountDownLatch(1); 535 } 536 537 private ParcelableConference parcelable(ConferenceInfo c) { 538 return new ParcelableConference( 539 c.phoneAccount, 540 c.state, 541 c.capabilities, 542 c.properties, 543 c.connectionIds, 544 c.videoProvider, 545 c.videoState, 546 c.connectTimeMillis, 547 c.statusHints, 548 c.extras); 549 } 550 551 private ParcelableConnection parcelable(ConnectionInfo c) { 552 return new ParcelableConnection( 553 c.request.getAccountHandle(), 554 c.state, 555 c.capabilities, 556 c.properties, 557 c.request.getAddress(), 558 c.addressPresentation, 559 c.callerDisplayName, 560 c.callerDisplayNamePresentation, 561 c.videoProvider, 562 c.videoState, 563 false, /* ringback requested */ 564 false, /* voip audio mode */ 565 0, /* Connect Time for conf call on this connection */ 566 c.statusHints, 567 c.disconnectCause, 568 c.conferenceableConnectionIds, 569 c.extras); 570 } 571} 572