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 17 package com.android.services.telephony; 18 19 import android.telecom.Conference; 20 import android.telecom.Connection; 21 import android.telecom.PhoneAccountHandle; 22 23 import com.android.internal.telephony.Call; 24 import com.android.internal.telephony.CallStateException; 25 import com.android.internal.telephony.Phone; 26 27 import java.util.List; 28 29 /** 30 * TelephonyConnection-based conference call for GSM conferences and IMS conferences (which may 31 * be either GSM-based or CDMA-based). 32 */ 33 public class TelephonyConference extends Conference implements Holdable { 34 35 private boolean mIsHoldable; 36 37 public TelephonyConference(PhoneAccountHandle phoneAccount) { 38 super(phoneAccount); 39 setConnectionCapabilities( 40 Connection.CAPABILITY_SUPPORT_HOLD | 41 Connection.CAPABILITY_HOLD | 42 Connection.CAPABILITY_MUTE | 43 Connection.CAPABILITY_MANAGE_CONFERENCE); 44 setActive(); 45 mIsHoldable = true; 46 } 47 48 /** 49 * Invoked when the Conference and all it's {@link Connection}s should be disconnected. 50 */ 51 @Override 52 public void onDisconnect() { 53 for (Connection connection : getConnections()) { 54 if (disconnectCall(connection)) { 55 break; 56 } 57 } 58 } 59 60 /** 61 * Disconnect the underlying Telephony Call for a connection. 62 * 63 * @param connection The connection. 64 * @return {@code True} if the call was disconnected. 65 */ 66 private boolean disconnectCall(Connection connection) { 67 Call call = getMultipartyCallForConnection(connection, "onDisconnect"); 68 if (call != null) { 69 Log.d(this, "Found multiparty call to hangup for conference."); 70 try { 71 call.hangup(); 72 return true; 73 } catch (CallStateException e) { 74 Log.e(this, e, "Exception thrown trying to hangup conference"); 75 } 76 } 77 return false; 78 } 79 80 /** 81 * Invoked when the specified {@link Connection} should be separated from the conference call. 82 * 83 * @param connection The connection to separate. 84 */ 85 @Override 86 public void onSeparate(Connection connection) { 87 com.android.internal.telephony.Connection radioConnection = 88 getOriginalConnection(connection); 89 try { 90 radioConnection.separate(); 91 } catch (CallStateException e) { 92 Log.e(this, e, "Exception thrown trying to separate a conference call"); 93 } 94 } 95 96 @Override 97 public void onMerge(Connection connection) { 98 try { 99 Phone phone = ((TelephonyConnection) connection).getPhone(); 100 if (phone != null) { 101 phone.conference(); 102 } 103 } catch (CallStateException e) { 104 Log.e(this, e, "Exception thrown trying to merge call into a conference"); 105 } 106 } 107 108 /** 109 * Invoked when the conference should be put on hold. 110 */ 111 @Override 112 public void onHold() { 113 final TelephonyConnection connection = getFirstConnection(); 114 if (connection != null) { 115 connection.performHold(); 116 } 117 } 118 119 /** 120 * Invoked when the conference should be moved from hold to active. 121 */ 122 @Override 123 public void onUnhold() { 124 final TelephonyConnection connection = getFirstConnection(); 125 if (connection != null) { 126 connection.performUnhold(); 127 } 128 } 129 130 @Override 131 public void onPlayDtmfTone(char c) { 132 final TelephonyConnection connection = getFirstConnection(); 133 if (connection != null) { 134 connection.onPlayDtmfTone(c); 135 } 136 } 137 138 @Override 139 public void onStopDtmfTone() { 140 final TelephonyConnection connection = getFirstConnection(); 141 if (connection != null) { 142 connection.onStopDtmfTone(); 143 } 144 } 145 146 @Override 147 public void onConnectionAdded(Connection connection) { 148 // If the conference was an IMS connection currently or before, disable MANAGE_CONFERENCE 149 // as the default behavior. If there is a conference event package, this may be overridden. 150 // If a conference event package was received, do not attempt to remove manage conference. 151 if (connection instanceof TelephonyConnection && 152 ((TelephonyConnection) connection).wasImsConnection()) { 153 removeCapability(Connection.CAPABILITY_MANAGE_CONFERENCE); 154 } 155 } 156 157 @Override 158 public Connection getPrimaryConnection() { 159 160 List<Connection> connections = getConnections(); 161 if (connections == null || connections.isEmpty()) { 162 return null; 163 } 164 165 // Default to the first connection. 166 Connection primaryConnection = connections.get(0); 167 168 // Otherwise look for a connection where the radio connection states it is multiparty. 169 for (Connection connection : connections) { 170 com.android.internal.telephony.Connection radioConnection = 171 getOriginalConnection(connection); 172 173 if (radioConnection != null && radioConnection.isMultiparty()) { 174 primaryConnection = connection; 175 break; 176 } 177 } 178 179 return primaryConnection; 180 } 181 182 @Override 183 public void setHoldable(boolean isHoldable) { 184 mIsHoldable = isHoldable; 185 if (!mIsHoldable) { 186 removeCapability(Connection.CAPABILITY_HOLD); 187 } else { 188 addCapability(Connection.CAPABILITY_HOLD); 189 } 190 } 191 192 @Override 193 public boolean isChildHoldable() { 194 // The conference should not be a child of other conference. 195 return false; 196 } 197 198 private Call getMultipartyCallForConnection(Connection connection, String tag) { 199 com.android.internal.telephony.Connection radioConnection = 200 getOriginalConnection(connection); 201 if (radioConnection != null) { 202 Call call = radioConnection.getCall(); 203 if (call != null && call.isMultiparty()) { 204 return call; 205 } 206 } 207 return null; 208 } 209 210 protected com.android.internal.telephony.Connection getOriginalConnection( 211 Connection connection) { 212 213 if (connection instanceof TelephonyConnection) { 214 return ((TelephonyConnection) connection).getOriginalConnection(); 215 } else { 216 return null; 217 } 218 } 219 220 private TelephonyConnection getFirstConnection() { 221 final List<Connection> connections = getConnections(); 222 if (connections.isEmpty()) { 223 return null; 224 } 225 return (TelephonyConnection) connections.get(0); 226 } 227 } 228