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