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