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