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