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