ImsConference.java revision f789e6e1f9e67e26d13305c494f00254b92f63f3
1/* 2 * Copyright (C) 2014 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.services.telephony; 18 19import android.content.Context; 20import android.graphics.drawable.Icon; 21import android.net.Uri; 22import android.telecom.Conference; 23import android.telecom.ConferenceParticipant; 24import android.telecom.Connection.VideoProvider; 25import android.telecom.Connection; 26import android.telecom.DisconnectCause; 27import android.telecom.Log; 28import android.telecom.PhoneAccountHandle; 29import android.telecom.StatusHints; 30import android.telecom.VideoProfile; 31 32import com.android.internal.telephony.Call; 33import com.android.internal.telephony.CallStateException; 34import com.android.internal.telephony.Phone; 35import com.android.internal.telephony.PhoneConstants; 36import com.android.internal.telephony.imsphone.ImsPhoneConnection; 37import com.android.phone.PhoneUtils; 38import com.android.phone.R; 39 40import java.util.ArrayList; 41import java.util.HashSet; 42import java.util.Iterator; 43import java.util.List; 44import java.util.Map; 45import java.util.concurrent.ConcurrentHashMap; 46 47/** 48 * Represents an IMS conference call. 49 * <p> 50 * An IMS conference call consists of a conference host connection and potentially a list of 51 * conference participants. The conference host connection represents the radio connection to the 52 * IMS conference server. Since it is not a connection to any one individual, it is not represented 53 * in Telecom/InCall as a call. The conference participant information is received via the host 54 * connection via a conference event package. Conference participant connections do not represent 55 * actual radio connections to the participants; they act as a virtual representation of the 56 * participant, keyed by a unique endpoint {@link android.net.Uri}. 57 * <p> 58 * The {@link ImsConference} listens for conference event package data received via the host 59 * connection and is responsible for managing the conference participant connections which represent 60 * the participants. 61 */ 62public class ImsConference extends Conference { 63 64 /** 65 * Listener used to respond to changes to conference participants. At the conference level we 66 * are most concerned with handling destruction of a conference participant. 67 */ 68 private final Connection.Listener mParticipantListener = new Connection.Listener() { 69 /** 70 * Participant has been destroyed. Remove it from the conference. 71 * 72 * @param connection The participant which was destroyed. 73 */ 74 @Override 75 public void onDestroyed(Connection connection) { 76 ConferenceParticipantConnection participant = 77 (ConferenceParticipantConnection) connection; 78 removeConferenceParticipant(participant); 79 updateManageConference(); 80 } 81 82 }; 83 84 /** 85 * Listener used to respond to changes to the underlying radio connection for the conference 86 * host connection. Used to respond to SRVCC changes. 87 */ 88 private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener = 89 new TelephonyConnection.TelephonyConnectionListener() { 90 91 @Override 92 public void onOriginalConnectionConfigured(TelephonyConnection c) { 93 if (c == mConferenceHost) { 94 handleOriginalConnectionChange(); 95 } 96 } 97 }; 98 99 /** 100 * Listener used to respond to changes to the connection to the IMS conference server. 101 */ 102 private final android.telecom.Connection.Listener mConferenceHostListener = 103 new android.telecom.Connection.Listener() { 104 105 /** 106 * Updates the state of the conference based on the new state of the host. 107 * 108 * @param c The host connection. 109 * @param state The new state 110 */ 111 @Override 112 public void onStateChanged(android.telecom.Connection c, int state) { 113 setState(state); 114 } 115 116 /** 117 * Disconnects the conference when its host connection disconnects. 118 * 119 * @param c The host connection. 120 * @param disconnectCause The host connection disconnect cause. 121 */ 122 @Override 123 public void onDisconnected(android.telecom.Connection c, DisconnectCause disconnectCause) { 124 setDisconnected(disconnectCause); 125 } 126 127 /** 128 * Handles destruction of the host connection; once the host connection has been 129 * destroyed, cleans up the conference participant connection. 130 * 131 * @param connection The host connection. 132 */ 133 @Override 134 public void onDestroyed(android.telecom.Connection connection) { 135 disconnectConferenceParticipants(); 136 } 137 138 /** 139 * Handles changes to conference participant data as reported by the conference host 140 * connection. 141 * 142 * @param c The connection. 143 * @param participants The participant information. 144 */ 145 @Override 146 public void onConferenceParticipantsChanged(android.telecom.Connection c, 147 List<ConferenceParticipant> participants) { 148 149 if (c == null || participants == null) { 150 return; 151 } 152 Log.v(this, "onConferenceParticipantsChanged: %d participants", participants.size()); 153 TelephonyConnection telephonyConnection = (TelephonyConnection) c; 154 handleConferenceParticipantsUpdate(telephonyConnection, participants); 155 } 156 157 @Override 158 public void onVideoStateChanged(android.telecom.Connection c, int videoState) { 159 Log.d(this, "onVideoStateChanged video state %d", videoState); 160 setVideoState(c, videoState); 161 } 162 163 @Override 164 public void onVideoProviderChanged(android.telecom.Connection c, 165 Connection.VideoProvider videoProvider) { 166 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c, 167 videoProvider); 168 setVideoProvider(c, videoProvider); 169 } 170 171 @Override 172 public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) { 173 Log.d(this, "onCallCapabilitiesChanged: Connection: %s, callCapabilities: %s", c, 174 connectionCapabilities); 175 int capabilites = ImsConference.this.getConnectionCapabilities(); 176 setConnectionCapabilities(applyVideoCapabilities(capabilites, connectionCapabilities)); 177 } 178 179 @Override 180 public void onStatusHintsChanged(Connection c, StatusHints statusHints) { 181 Log.v(this, "onStatusHintsChanged"); 182 updateStatusHints(); 183 } 184 }; 185 186 /** 187 * The telephony connection service; used to add new participant connections to Telecom. 188 */ 189 private TelephonyConnectionService mTelephonyConnectionService; 190 191 /** 192 * The connection to the conference server which is hosting the conference. 193 */ 194 private TelephonyConnection mConferenceHost; 195 196 /** 197 * The known conference participant connections. The HashMap is keyed by endpoint Uri. 198 * A {@link ConcurrentHashMap} is used as there is a possibility for radio events impacting the 199 * available participants to occur at the same time as an access via the connection service. 200 */ 201 private final ConcurrentHashMap<Uri, ConferenceParticipantConnection> 202 mConferenceParticipantConnections = 203 new ConcurrentHashMap<Uri, ConferenceParticipantConnection>(8, 0.9f, 1); 204 205 public void updateConferenceParticipantsAfterCreation() { 206 if (mConferenceHost != null) { 207 Log.v(this, "updateConferenceStateAfterCreation :: process participant update"); 208 handleConferenceParticipantsUpdate(mConferenceHost, 209 mConferenceHost.getConferenceParticipants()); 210 } else { 211 Log.v(this, "updateConferenceStateAfterCreation :: null mConferenceHost"); 212 } 213 } 214 215 /** 216 * Initializes a new {@link ImsConference}. 217 * 218 * @param telephonyConnectionService The connection service responsible for adding new 219 * conferene participants. 220 * @param conferenceHost The telephony connection hosting the conference. 221 */ 222 public ImsConference(TelephonyConnectionService telephonyConnectionService, 223 TelephonyConnection conferenceHost) { 224 225 super((conferenceHost != null && conferenceHost.getCall() != null && 226 conferenceHost.getCall().getPhone() != null) ? 227 PhoneUtils.makePstnPhoneAccountHandle( 228 conferenceHost.getCall().getPhone()) : null); 229 230 // Specify the connection time of the conference to be the connection time of the original 231 // connection. 232 long connectTime = conferenceHost.getOriginalConnection().getConnectTime(); 233 setConnectTimeMillis(connectTime); 234 // Set the connectTime in the connection as well. 235 conferenceHost.setConnectTimeMillis(connectTime); 236 237 mTelephonyConnectionService = telephonyConnectionService; 238 setConferenceHost(conferenceHost); 239 240 int capabilities = Connection.CAPABILITY_SUPPORT_HOLD | Connection.CAPABILITY_HOLD | 241 Connection.CAPABILITY_MUTE | Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN; 242 243 capabilities = applyVideoCapabilities(capabilities, mConferenceHost.getConnectionCapabilities()); 244 setConnectionCapabilities(capabilities); 245 246 } 247 248 private int applyVideoCapabilities(int conferenceCapabilities, int capabilities) { 249 if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) { 250 conferenceCapabilities = applyCapability(conferenceCapabilities, 251 Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL); 252 } else { 253 conferenceCapabilities = removeCapability(conferenceCapabilities, 254 Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL); 255 } 256 257 if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) { 258 conferenceCapabilities = applyCapability(conferenceCapabilities, 259 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 260 } else { 261 conferenceCapabilities = removeCapability(conferenceCapabilities, 262 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 263 } 264 265 if (can(capabilities, Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO)) { 266 conferenceCapabilities = applyCapability(conferenceCapabilities, 267 Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO); 268 } else { 269 conferenceCapabilities = removeCapability(conferenceCapabilities, 270 Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO); 271 } 272 return conferenceCapabilities; 273 } 274 275 /** 276 * Not used by the IMS conference controller. 277 * 278 * @return {@code Null}. 279 */ 280 @Override 281 public android.telecom.Connection getPrimaryConnection() { 282 return null; 283 } 284 285 /** 286 * Returns VideoProvider of the conference. This can be null. 287 * 288 * @hide 289 */ 290 @Override 291 public VideoProvider getVideoProvider() { 292 if (mConferenceHost != null) { 293 return mConferenceHost.getVideoProvider(); 294 } 295 return null; 296 } 297 298 /** 299 * Returns video state of conference 300 * 301 * @hide 302 */ 303 @Override 304 public int getVideoState() { 305 if (mConferenceHost != null) { 306 return mConferenceHost.getVideoState(); 307 } 308 return VideoProfile.STATE_AUDIO_ONLY; 309 } 310 311 /** 312 * Invoked when the Conference and all its {@link Connection}s should be disconnected. 313 * <p> 314 * Hangs up the call via the conference host connection. When the host connection has been 315 * successfully disconnected, the {@link #mConferenceHostListener} listener receives an 316 * {@code onDestroyed} event, which triggers the conference participant connections to be 317 * disconnected. 318 */ 319 @Override 320 public void onDisconnect() { 321 Log.v(this, "onDisconnect: hanging up conference host."); 322 if (mConferenceHost == null) { 323 return; 324 } 325 326 Call call = mConferenceHost.getCall(); 327 if (call != null) { 328 try { 329 call.hangup(); 330 } catch (CallStateException e) { 331 Log.e(this, e, "Exception thrown trying to hangup conference"); 332 } 333 } 334 } 335 336 /** 337 * Invoked when the specified {@link android.telecom.Connection} should be separated from the 338 * conference call. 339 * <p> 340 * IMS does not support separating connections from the conference. 341 * 342 * @param connection The connection to separate. 343 */ 344 @Override 345 public void onSeparate(android.telecom.Connection connection) { 346 Log.wtf(this, "Cannot separate connections from an IMS conference."); 347 } 348 349 /** 350 * Invoked when the specified {@link android.telecom.Connection} should be merged into the 351 * conference call. 352 * 353 * @param connection The {@code Connection} to merge. 354 */ 355 @Override 356 public void onMerge(android.telecom.Connection connection) { 357 try { 358 Phone phone = ((TelephonyConnection) connection).getPhone(); 359 if (phone != null) { 360 phone.conference(); 361 } 362 } catch (CallStateException e) { 363 Log.e(this, e, "Exception thrown trying to merge call into a conference"); 364 } 365 } 366 367 /** 368 * Invoked when the conference should be put on hold. 369 */ 370 @Override 371 public void onHold() { 372 if (mConferenceHost == null) { 373 return; 374 } 375 mConferenceHost.performHold(); 376 } 377 378 /** 379 * Invoked when the conference should be moved from hold to active. 380 */ 381 @Override 382 public void onUnhold() { 383 if (mConferenceHost == null) { 384 return; 385 } 386 mConferenceHost.performUnhold(); 387 } 388 389 /** 390 * Invoked to play a DTMF tone. 391 * 392 * @param c A DTMF character. 393 */ 394 @Override 395 public void onPlayDtmfTone(char c) { 396 if (mConferenceHost == null) { 397 return; 398 } 399 mConferenceHost.onPlayDtmfTone(c); 400 } 401 402 /** 403 * Invoked to stop playing a DTMF tone. 404 */ 405 @Override 406 public void onStopDtmfTone() { 407 if (mConferenceHost == null) { 408 return; 409 } 410 mConferenceHost.onStopDtmfTone(); 411 } 412 413 /** 414 * Handles the addition of connections to the {@link ImsConference}. The 415 * {@link ImsConferenceController} does not add connections to the conference. 416 * 417 * @param connection The newly added connection. 418 */ 419 @Override 420 public void onConnectionAdded(android.telecom.Connection connection) { 421 // No-op 422 } 423 424 private int applyCapability(int capabilities, int capability) { 425 int newCapabilities = capabilities | capability; 426 return newCapabilities; 427 } 428 429 private int removeCapability(int capabilities, int capability) { 430 int newCapabilities = capabilities & ~capability; 431 return newCapabilities; 432 } 433 434 /** 435 * Determines if this conference is hosted on the current device or the peer device. 436 * 437 * @return {@code true} if this conference is hosted on the current device, {@code false} if it 438 * is hosted on the peer device. 439 */ 440 public boolean isConferenceHost() { 441 if (mConferenceHost == null) { 442 return false; 443 } 444 com.android.internal.telephony.Connection originalConnection = 445 mConferenceHost.getOriginalConnection(); 446 if (!(originalConnection instanceof ImsPhoneConnection)) { 447 return false; 448 } 449 450 ImsPhoneConnection imsPhoneConnection = (ImsPhoneConnection) originalConnection; 451 return imsPhoneConnection.isMultiparty() && imsPhoneConnection.isConferenceHost(); 452 } 453 454 /** 455 * Updates the manage conference capability of the conference. Where there are one or more 456 * conference event package participants, the conference management is permitted. Where there 457 * are no conference event package participants, conference management is not permitted. 458 * <p> 459 * Note: We add and remove {@link Connection#CAPABILITY_CONFERENCE_HAS_NO_CHILDREN} to ensure 460 * that the conference is represented appropriately on Bluetooth devices. 461 */ 462 private void updateManageConference() { 463 boolean couldManageConference = can(Connection.CAPABILITY_MANAGE_CONFERENCE); 464 boolean canManageConference = !mConferenceParticipantConnections.isEmpty(); 465 Log.v(this, "updateManageConference was :%s is:%s", couldManageConference ? "Y" : "N", 466 canManageConference ? "Y" : "N"); 467 468 if (couldManageConference != canManageConference) { 469 int capabilities = getConnectionCapabilities(); 470 471 if (canManageConference) { 472 capabilities |= Connection.CAPABILITY_MANAGE_CONFERENCE; 473 capabilities &= ~Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN; 474 } else { 475 capabilities &= ~Connection.CAPABILITY_MANAGE_CONFERENCE; 476 capabilities |= Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN; 477 } 478 479 setConnectionCapabilities(capabilities); 480 } 481 } 482 483 /** 484 * Sets the connection hosting the conference and registers for callbacks. 485 * 486 * @param conferenceHost The connection hosting the conference. 487 */ 488 private void setConferenceHost(TelephonyConnection conferenceHost) { 489 if (Log.VERBOSE) { 490 Log.v(this, "setConferenceHost " + conferenceHost); 491 } 492 493 mConferenceHost = conferenceHost; 494 mConferenceHost.addConnectionListener(mConferenceHostListener); 495 mConferenceHost.addTelephonyConnectionListener(mTelephonyConnectionListener); 496 setState(mConferenceHost.getState()); 497 updateStatusHints(); 498 } 499 500 /** 501 * Handles state changes for conference participant(s). The participants data passed in 502 * 503 * @param parent The connection which was notified of the conference participant. 504 * @param participants The conference participant information. 505 */ 506 private void handleConferenceParticipantsUpdate( 507 TelephonyConnection parent, List<ConferenceParticipant> participants) { 508 509 if (participants == null) { 510 return; 511 } 512 boolean newParticipantsAdded = false; 513 boolean oldParticipantsRemoved = false; 514 ArrayList<ConferenceParticipant> newParticipants = new ArrayList<>(participants.size()); 515 HashSet<Uri> participantUserEntities = new HashSet<>(participants.size()); 516 517 // Add any new participants and update existing. 518 for (ConferenceParticipant participant : participants) { 519 Uri userEntity = participant.getHandle(); 520 521 participantUserEntities.add(userEntity); 522 if (!mConferenceParticipantConnections.containsKey(userEntity)) { 523 createConferenceParticipantConnection(parent, participant); 524 newParticipants.add(participant); 525 newParticipantsAdded = true; 526 } else { 527 ConferenceParticipantConnection connection = 528 mConferenceParticipantConnections.get(userEntity); 529 connection.updateState(participant.getState()); 530 } 531 } 532 533 // Set state of new participants. 534 if (newParticipantsAdded) { 535 // Set the state of the new participants at once and add to the conference 536 for (ConferenceParticipant newParticipant : newParticipants) { 537 ConferenceParticipantConnection connection = 538 mConferenceParticipantConnections.get(newParticipant.getHandle()); 539 connection.updateState(newParticipant.getState()); 540 } 541 } 542 543 // Finally, remove any participants from the conference that no longer exist in the 544 // conference event package data. 545 Iterator<Map.Entry<Uri, ConferenceParticipantConnection>> entryIterator = 546 mConferenceParticipantConnections.entrySet().iterator(); 547 while (entryIterator.hasNext()) { 548 Map.Entry<Uri, ConferenceParticipantConnection> entry = entryIterator.next(); 549 550 if (!participantUserEntities.contains(entry.getKey())) { 551 ConferenceParticipantConnection participant = entry.getValue(); 552 participant.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED)); 553 participant.removeConnectionListener(mParticipantListener); 554 mTelephonyConnectionService.removeConnection(participant); 555 removeConnection(participant); 556 entryIterator.remove(); 557 oldParticipantsRemoved = true; 558 } 559 } 560 561 // If new participants were added or old ones were removed, we need to ensure the state of 562 // the manage conference capability is updated. 563 if (newParticipantsAdded || oldParticipantsRemoved) { 564 updateManageConference(); 565 } 566 } 567 568 /** 569 * Creates a new {@link ConferenceParticipantConnection} to represent a 570 * {@link ConferenceParticipant}. 571 * <p> 572 * The new connection is added to the conference controller and connection service. 573 * 574 * @param parent The connection which was notified of the participant change (e.g. the 575 * parent connection). 576 * @param participant The conference participant information. 577 */ 578 private void createConferenceParticipantConnection( 579 TelephonyConnection parent, ConferenceParticipant participant) { 580 581 // Create and add the new connection in holding state so that it does not become the 582 // active call. 583 ConferenceParticipantConnection connection = new ConferenceParticipantConnection( 584 parent.getOriginalConnection(), participant); 585 connection.addConnectionListener(mParticipantListener); 586 connection.setConnectTimeMillis(parent.getConnectTimeMillis()); 587 588 if (Log.VERBOSE) { 589 Log.v(this, "createConferenceParticipantConnection: %s", connection); 590 } 591 592 mConferenceParticipantConnections.put(participant.getHandle(), connection); 593 PhoneAccountHandle phoneAccountHandle = 594 PhoneUtils.makePstnPhoneAccountHandle(parent.getPhone()); 595 mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, connection); 596 addConnection(connection); 597 } 598 599 /** 600 * Removes a conference participant from the conference. 601 * 602 * @param participant The participant to remove. 603 */ 604 private void removeConferenceParticipant(ConferenceParticipantConnection participant) { 605 Log.d(this, "removeConferenceParticipant: %s", participant); 606 607 participant.removeConnectionListener(mParticipantListener); 608 mConferenceParticipantConnections.remove(participant.getUserEntity()); 609 mTelephonyConnectionService.removeConnection(participant); 610 } 611 612 /** 613 * Disconnects all conference participants from the conference. 614 */ 615 private void disconnectConferenceParticipants() { 616 Log.v(this, "disconnectConferenceParticipants"); 617 618 for (ConferenceParticipantConnection connection : 619 mConferenceParticipantConnections.values()) { 620 621 connection.removeConnectionListener(mParticipantListener); 622 // Mark disconnect cause as cancelled to ensure that the call is not logged in the 623 // call log. 624 connection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED)); 625 mTelephonyConnectionService.removeConnection(connection); 626 connection.destroy(); 627 } 628 mConferenceParticipantConnections.clear(); 629 } 630 631 /** 632 * Handles a change in the original connection backing the conference host connection. This can 633 * happen if an SRVCC event occurs on the original IMS connection, requiring a fallback to 634 * GSM or CDMA. 635 * <p> 636 * If this happens, we will add the conference host connection to telecom and tear down the 637 * conference. 638 */ 639 private void handleOriginalConnectionChange() { 640 if (mConferenceHost == null) { 641 Log.w(this, "handleOriginalConnectionChange; conference host missing."); 642 return; 643 } 644 645 com.android.internal.telephony.Connection originalConnection = 646 mConferenceHost.getOriginalConnection(); 647 648 if (!(originalConnection instanceof ImsPhoneConnection)) { 649 if (Log.VERBOSE) { 650 Log.v(this, 651 "Original connection for conference host is no longer an IMS connection; " + 652 "new connection: %s", originalConnection); 653 } 654 655 PhoneAccountHandle phoneAccountHandle = 656 PhoneUtils.makePstnPhoneAccountHandle(mConferenceHost.getPhone()); 657 if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) { 658 GsmConnection c = new GsmConnection(originalConnection); 659 c.updateState(); 660 mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, c); 661 mTelephonyConnectionService.addConnectionToConferenceController(c); 662 } // CDMA case not applicable for SRVCC 663 mConferenceHost.removeConnectionListener(mConferenceHostListener); 664 mConferenceHost.removeTelephonyConnectionListener(mTelephonyConnectionListener); 665 mConferenceHost = null; 666 setDisconnected(new DisconnectCause(DisconnectCause.OTHER)); 667 disconnectConferenceParticipants(); 668 destroy(); 669 } 670 671 updateStatusHints(); 672 } 673 674 /** 675 * Changes the state of the Ims conference. 676 * 677 * @param state the new state. 678 */ 679 public void setState(int state) { 680 Log.v(this, "setState %s", Connection.stateToString(state)); 681 682 switch (state) { 683 case Connection.STATE_INITIALIZING: 684 case Connection.STATE_NEW: 685 case Connection.STATE_RINGING: 686 // No-op -- not applicable. 687 break; 688 case Connection.STATE_DIALING: 689 setDialing(); 690 break; 691 case Connection.STATE_DISCONNECTED: 692 DisconnectCause disconnectCause; 693 if (mConferenceHost == null) { 694 disconnectCause = new DisconnectCause(DisconnectCause.CANCELED); 695 } else { 696 disconnectCause = DisconnectCauseUtil.toTelecomDisconnectCause( 697 mConferenceHost.getOriginalConnection().getDisconnectCause()); 698 } 699 setDisconnected(disconnectCause); 700 destroy(); 701 break; 702 case Connection.STATE_ACTIVE: 703 setActive(); 704 break; 705 case Connection.STATE_HOLDING: 706 setOnHold(); 707 break; 708 } 709 } 710 711 private void updateStatusHints() { 712 if (mConferenceHost == null) { 713 setStatusHints(null); 714 return; 715 } 716 717 if (mConferenceHost.isWifi()) { 718 Phone phone = mConferenceHost.getPhone(); 719 if (phone != null) { 720 Context context = phone.getContext(); 721 setStatusHints(new StatusHints( 722 context.getString(R.string.status_hint_label_wifi_call), 723 Icon.createWithResource( 724 context.getResources(), 725 R.drawable.ic_signal_wifi_4_bar_24dp), 726 null /* extras */)); 727 } 728 } else { 729 setStatusHints(null); 730 } 731 } 732 733 /** 734 * Builds a string representation of the {@link ImsConference}. 735 * 736 * @return String representing the conference. 737 */ 738 public String toString() { 739 StringBuilder sb = new StringBuilder(); 740 sb.append("[ImsConference objId:"); 741 sb.append(System.identityHashCode(this)); 742 sb.append(" state:"); 743 sb.append(Connection.stateToString(getState())); 744 sb.append(" hostConnection:"); 745 sb.append(mConferenceHost); 746 sb.append(" participants:"); 747 sb.append(mConferenceParticipantConnections.size()); 748 sb.append("]"); 749 return sb.toString(); 750 } 751} 752