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