ImsConference.java revision cd3b79cc1d3fbb5f77ab17fc2756c75fe7ff9ef7
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 setConnectTimeMillis(conferenceHost.getOriginalConnection().getConnectTime()); 233 234 mTelephonyConnectionService = telephonyConnectionService; 235 setConferenceHost(conferenceHost); 236 237 int capabilities = Connection.CAPABILITY_SUPPORT_HOLD | Connection.CAPABILITY_HOLD | 238 Connection.CAPABILITY_MUTE | Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN; 239 240 capabilities = applyVideoCapabilities(capabilities, mConferenceHost.getConnectionCapabilities()); 241 setConnectionCapabilities(capabilities); 242 243 } 244 245 private int applyVideoCapabilities(int conferenceCapabilities, int capabilities) { 246 if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) { 247 conferenceCapabilities = applyCapability(conferenceCapabilities, 248 Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL); 249 } else { 250 conferenceCapabilities = removeCapability(conferenceCapabilities, 251 Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL); 252 } 253 254 if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) { 255 conferenceCapabilities = applyCapability(conferenceCapabilities, 256 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 257 } else { 258 conferenceCapabilities = removeCapability(conferenceCapabilities, 259 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 260 } 261 262 if (can(capabilities, Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO)) { 263 conferenceCapabilities = applyCapability(conferenceCapabilities, 264 Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO); 265 } else { 266 conferenceCapabilities = removeCapability(conferenceCapabilities, 267 Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO); 268 } 269 return conferenceCapabilities; 270 } 271 272 /** 273 * Not used by the IMS conference controller. 274 * 275 * @return {@code Null}. 276 */ 277 @Override 278 public android.telecom.Connection getPrimaryConnection() { 279 return null; 280 } 281 282 /** 283 * Returns VideoProvider of the conference. This can be null. 284 * 285 * @hide 286 */ 287 @Override 288 public VideoProvider getVideoProvider() { 289 if (mConferenceHost != null) { 290 return mConferenceHost.getVideoProvider(); 291 } 292 return null; 293 } 294 295 /** 296 * Returns video state of conference 297 * 298 * @hide 299 */ 300 @Override 301 public int getVideoState() { 302 if (mConferenceHost != null) { 303 return mConferenceHost.getVideoState(); 304 } 305 return VideoProfile.STATE_AUDIO_ONLY; 306 } 307 308 /** 309 * Invoked when the Conference and all its {@link Connection}s should be disconnected. 310 * <p> 311 * Hangs up the call via the conference host connection. When the host connection has been 312 * successfully disconnected, the {@link #mConferenceHostListener} listener receives an 313 * {@code onDestroyed} event, which triggers the conference participant connections to be 314 * disconnected. 315 */ 316 @Override 317 public void onDisconnect() { 318 Log.v(this, "onDisconnect: hanging up conference host."); 319 if (mConferenceHost == null) { 320 return; 321 } 322 323 Call call = mConferenceHost.getCall(); 324 if (call != null) { 325 try { 326 call.hangup(); 327 } catch (CallStateException e) { 328 Log.e(this, e, "Exception thrown trying to hangup conference"); 329 } 330 } 331 } 332 333 /** 334 * Invoked when the specified {@link android.telecom.Connection} should be separated from the 335 * conference call. 336 * <p> 337 * IMS does not support separating connections from the conference. 338 * 339 * @param connection The connection to separate. 340 */ 341 @Override 342 public void onSeparate(android.telecom.Connection connection) { 343 Log.wtf(this, "Cannot separate connections from an IMS conference."); 344 } 345 346 /** 347 * Invoked when the specified {@link android.telecom.Connection} should be merged into the 348 * conference call. 349 * 350 * @param connection The {@code Connection} to merge. 351 */ 352 @Override 353 public void onMerge(android.telecom.Connection connection) { 354 try { 355 Phone phone = ((TelephonyConnection) connection).getPhone(); 356 if (phone != null) { 357 phone.conference(); 358 } 359 } catch (CallStateException e) { 360 Log.e(this, e, "Exception thrown trying to merge call into a conference"); 361 } 362 } 363 364 /** 365 * Invoked when the conference should be put on hold. 366 */ 367 @Override 368 public void onHold() { 369 if (mConferenceHost == null) { 370 return; 371 } 372 mConferenceHost.performHold(); 373 } 374 375 /** 376 * Invoked when the conference should be moved from hold to active. 377 */ 378 @Override 379 public void onUnhold() { 380 if (mConferenceHost == null) { 381 return; 382 } 383 mConferenceHost.performUnhold(); 384 } 385 386 /** 387 * Invoked to play a DTMF tone. 388 * 389 * @param c A DTMF character. 390 */ 391 @Override 392 public void onPlayDtmfTone(char c) { 393 if (mConferenceHost == null) { 394 return; 395 } 396 mConferenceHost.onPlayDtmfTone(c); 397 } 398 399 /** 400 * Invoked to stop playing a DTMF tone. 401 */ 402 @Override 403 public void onStopDtmfTone() { 404 if (mConferenceHost == null) { 405 return; 406 } 407 mConferenceHost.onStopDtmfTone(); 408 } 409 410 /** 411 * Handles the addition of connections to the {@link ImsConference}. The 412 * {@link ImsConferenceController} does not add connections to the conference. 413 * 414 * @param connection The newly added connection. 415 */ 416 @Override 417 public void onConnectionAdded(android.telecom.Connection connection) { 418 // No-op 419 } 420 421 private int applyCapability(int capabilities, int capability) { 422 int newCapabilities = capabilities | capability; 423 return newCapabilities; 424 } 425 426 private int removeCapability(int capabilities, int capability) { 427 int newCapabilities = capabilities & ~capability; 428 return newCapabilities; 429 } 430 431 /** 432 * Determines if this conference is hosted on the current device or the peer device. 433 * 434 * @return {@code true} if this conference is hosted on the current device, {@code false} if it 435 * is hosted on the peer device. 436 */ 437 public boolean isConferenceHost() { 438 if (mConferenceHost == null) { 439 return false; 440 } 441 com.android.internal.telephony.Connection originalConnection = 442 mConferenceHost.getOriginalConnection(); 443 if (!(originalConnection instanceof ImsPhoneConnection)) { 444 return false; 445 } 446 447 ImsPhoneConnection imsPhoneConnection = (ImsPhoneConnection) originalConnection; 448 return imsPhoneConnection.isMultiparty() && imsPhoneConnection.isConferenceHost(); 449 } 450 451 /** 452 * Updates the manage conference capability of the conference. Where there are one or more 453 * conference event package participants, the conference management is permitted. Where there 454 * are no conference event package participants, conference management is not permitted. 455 * <p> 456 * Note: We add and remove {@link Connection#CAPABILITY_CONFERENCE_HAS_NO_CHILDREN} to ensure 457 * that the conference is represented appropriately on Bluetooth devices. 458 */ 459 private void updateManageConference() { 460 boolean couldManageConference = can(Connection.CAPABILITY_MANAGE_CONFERENCE); 461 boolean canManageConference = !mConferenceParticipantConnections.isEmpty(); 462 Log.v(this, "updateManageConference was :%s is:%s", couldManageConference ? "Y" : "N", 463 canManageConference ? "Y" : "N"); 464 465 if (couldManageConference != canManageConference) { 466 int capabilities = getConnectionCapabilities(); 467 468 if (canManageConference) { 469 capabilities |= Connection.CAPABILITY_MANAGE_CONFERENCE; 470 capabilities &= ~Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN; 471 } else { 472 capabilities &= ~Connection.CAPABILITY_MANAGE_CONFERENCE; 473 capabilities |= Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN; 474 } 475 476 setConnectionCapabilities(capabilities); 477 } 478 } 479 480 /** 481 * Sets the connection hosting the conference and registers for callbacks. 482 * 483 * @param conferenceHost The connection hosting the conference. 484 */ 485 private void setConferenceHost(TelephonyConnection conferenceHost) { 486 if (Log.VERBOSE) { 487 Log.v(this, "setConferenceHost " + conferenceHost); 488 } 489 490 mConferenceHost = conferenceHost; 491 mConferenceHost.addConnectionListener(mConferenceHostListener); 492 mConferenceHost.addTelephonyConnectionListener(mTelephonyConnectionListener); 493 setState(mConferenceHost.getState()); 494 updateStatusHints(); 495 } 496 497 /** 498 * Handles state changes for conference participant(s). The participants data passed in 499 * 500 * @param parent The connection which was notified of the conference participant. 501 * @param participants The conference participant information. 502 */ 503 private void handleConferenceParticipantsUpdate( 504 TelephonyConnection parent, List<ConferenceParticipant> participants) { 505 506 if (participants == null) { 507 return; 508 } 509 boolean newParticipantsAdded = false; 510 boolean oldParticipantsRemoved = false; 511 ArrayList<ConferenceParticipant> newParticipants = new ArrayList<>(participants.size()); 512 HashSet<Uri> participantUserEntities = new HashSet<>(participants.size()); 513 514 // Add any new participants and update existing. 515 for (ConferenceParticipant participant : participants) { 516 Uri userEntity = participant.getHandle(); 517 518 participantUserEntities.add(userEntity); 519 if (!mConferenceParticipantConnections.containsKey(userEntity)) { 520 createConferenceParticipantConnection(parent, participant); 521 newParticipants.add(participant); 522 newParticipantsAdded = true; 523 } else { 524 ConferenceParticipantConnection connection = 525 mConferenceParticipantConnections.get(userEntity); 526 connection.updateState(participant.getState()); 527 } 528 } 529 530 // Set state of new participants. 531 if (newParticipantsAdded) { 532 // Set the state of the new participants at once and add to the conference 533 for (ConferenceParticipant newParticipant : newParticipants) { 534 ConferenceParticipantConnection connection = 535 mConferenceParticipantConnections.get(newParticipant.getHandle()); 536 connection.updateState(newParticipant.getState()); 537 } 538 } 539 540 // Finally, remove any participants from the conference that no longer exist in the 541 // conference event package data. 542 Iterator<Map.Entry<Uri, ConferenceParticipantConnection>> entryIterator = 543 mConferenceParticipantConnections.entrySet().iterator(); 544 while (entryIterator.hasNext()) { 545 Map.Entry<Uri, ConferenceParticipantConnection> entry = entryIterator.next(); 546 547 if (!participantUserEntities.contains(entry.getKey())) { 548 ConferenceParticipantConnection participant = entry.getValue(); 549 participant.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED)); 550 participant.removeConnectionListener(mParticipantListener); 551 mTelephonyConnectionService.removeConnection(participant); 552 removeConnection(participant); 553 entryIterator.remove(); 554 oldParticipantsRemoved = true; 555 } 556 } 557 558 // If new participants were added or old ones were removed, we need to ensure the state of 559 // the manage conference capability is updated. 560 if (newParticipantsAdded || oldParticipantsRemoved) { 561 updateManageConference(); 562 } 563 } 564 565 /** 566 * Creates a new {@link ConferenceParticipantConnection} to represent a 567 * {@link ConferenceParticipant}. 568 * <p> 569 * The new connection is added to the conference controller and connection service. 570 * 571 * @param parent The connection which was notified of the participant change (e.g. the 572 * parent connection). 573 * @param participant The conference participant information. 574 */ 575 private void createConferenceParticipantConnection( 576 TelephonyConnection parent, ConferenceParticipant participant) { 577 578 // Create and add the new connection in holding state so that it does not become the 579 // active call. 580 ConferenceParticipantConnection connection = new ConferenceParticipantConnection( 581 parent.getOriginalConnection(), participant); 582 connection.addConnectionListener(mParticipantListener); 583 584 if (Log.VERBOSE) { 585 Log.v(this, "createConferenceParticipantConnection: %s", connection); 586 } 587 588 mConferenceParticipantConnections.put(participant.getHandle(), connection); 589 PhoneAccountHandle phoneAccountHandle = 590 PhoneUtils.makePstnPhoneAccountHandle(parent.getPhone()); 591 mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, connection); 592 addConnection(connection); 593 } 594 595 /** 596 * Removes a conference participant from the conference. 597 * 598 * @param participant The participant to remove. 599 */ 600 private void removeConferenceParticipant(ConferenceParticipantConnection participant) { 601 Log.d(this, "removeConferenceParticipant: %s", participant); 602 603 participant.removeConnectionListener(mParticipantListener); 604 mConferenceParticipantConnections.remove(participant.getUserEntity()); 605 mTelephonyConnectionService.removeConnection(participant); 606 } 607 608 /** 609 * Disconnects all conference participants from the conference. 610 */ 611 private void disconnectConferenceParticipants() { 612 Log.v(this, "disconnectConferenceParticipants"); 613 614 for (ConferenceParticipantConnection connection : 615 mConferenceParticipantConnections.values()) { 616 617 connection.removeConnectionListener(mParticipantListener); 618 // Mark disconnect cause as cancelled to ensure that the call is not logged in the 619 // call log. 620 connection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED)); 621 mTelephonyConnectionService.removeConnection(connection); 622 connection.destroy(); 623 } 624 mConferenceParticipantConnections.clear(); 625 } 626 627 /** 628 * Handles a change in the original connection backing the conference host connection. This can 629 * happen if an SRVCC event occurs on the original IMS connection, requiring a fallback to 630 * GSM or CDMA. 631 * <p> 632 * If this happens, we will add the conference host connection to telecom and tear down the 633 * conference. 634 */ 635 private void handleOriginalConnectionChange() { 636 if (mConferenceHost == null) { 637 Log.w(this, "handleOriginalConnectionChange; conference host missing."); 638 return; 639 } 640 641 com.android.internal.telephony.Connection originalConnection = 642 mConferenceHost.getOriginalConnection(); 643 644 if (!(originalConnection instanceof ImsPhoneConnection)) { 645 if (Log.VERBOSE) { 646 Log.v(this, 647 "Original connection for conference host is no longer an IMS connection; " + 648 "new connection: %s", originalConnection); 649 } 650 651 PhoneAccountHandle phoneAccountHandle = 652 PhoneUtils.makePstnPhoneAccountHandle(mConferenceHost.getPhone()); 653 if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) { 654 GsmConnection c = new GsmConnection(originalConnection); 655 c.updateState(); 656 mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, c); 657 mTelephonyConnectionService.addConnectionToConferenceController(c); 658 } // CDMA case not applicable for SRVCC 659 mConferenceHost.removeConnectionListener(mConferenceHostListener); 660 mConferenceHost.removeTelephonyConnectionListener(mTelephonyConnectionListener); 661 mConferenceHost = null; 662 setDisconnected(new DisconnectCause(DisconnectCause.OTHER)); 663 destroy(); 664 } 665 666 updateStatusHints(); 667 } 668 669 /** 670 * Changes the state of the Ims conference. 671 * 672 * @param state the new state. 673 */ 674 public void setState(int state) { 675 Log.v(this, "setState %s", Connection.stateToString(state)); 676 677 switch (state) { 678 case Connection.STATE_INITIALIZING: 679 case Connection.STATE_NEW: 680 case Connection.STATE_RINGING: 681 // No-op -- not applicable. 682 break; 683 case Connection.STATE_DIALING: 684 setDialing(); 685 break; 686 case Connection.STATE_DISCONNECTED: 687 DisconnectCause disconnectCause; 688 if (mConferenceHost == null) { 689 disconnectCause = new DisconnectCause(DisconnectCause.CANCELED); 690 } else { 691 disconnectCause = DisconnectCauseUtil.toTelecomDisconnectCause( 692 mConferenceHost.getOriginalConnection().getDisconnectCause()); 693 } 694 setDisconnected(disconnectCause); 695 destroy(); 696 break; 697 case Connection.STATE_ACTIVE: 698 setActive(); 699 break; 700 case Connection.STATE_HOLDING: 701 setOnHold(); 702 break; 703 } 704 } 705 706 private void updateStatusHints() { 707 if (mConferenceHost == null) { 708 setStatusHints(null); 709 return; 710 } 711 712 if (mConferenceHost.isWifi()) { 713 Phone phone = mConferenceHost.getPhone(); 714 if (phone != null) { 715 Context context = phone.getContext(); 716 setStatusHints(new StatusHints( 717 context.getString(R.string.status_hint_label_wifi_call), 718 Icon.createWithResource( 719 context.getResources(), 720 R.drawable.ic_signal_wifi_4_bar_24dp), 721 null /* extras */)); 722 } 723 } else { 724 setStatusHints(null); 725 } 726 } 727 728 /** 729 * Builds a string representation of the {@link ImsConference}. 730 * 731 * @return String representing the conference. 732 */ 733 public String toString() { 734 StringBuilder sb = new StringBuilder(); 735 sb.append("[ImsConference objId:"); 736 sb.append(System.identityHashCode(this)); 737 sb.append(" state:"); 738 sb.append(Connection.stateToString(getState())); 739 sb.append(" hostConnection:"); 740 sb.append(mConferenceHost); 741 sb.append(" participants:"); 742 sb.append(mConferenceParticipantConnections.size()); 743 sb.append("]"); 744 return sb.toString(); 745 } 746} 747