1aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad/* 2aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * Copyright (C) 2014 The Android Open Source Project 3aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * 4aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * Licensed under the Apache License, Version 2.0 (the "License"); 5aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * you may not use this file except in compliance with the License. 6aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * You may obtain a copy of the License at 7aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * 8aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * http://www.apache.org/licenses/LICENSE-2.0 9aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * 10aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * Unless required by applicable law or agreed to in writing, software 11aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * distributed under the License is distributed on an "AS IS" BASIS, 12aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * See the License for the specific language governing permissions and 14aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * limitations under the License. 15aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad */ 16aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 17aa7340388473c1676495a60e30dc6a48d318a489Ihab Awadpackage com.android.services.telephony; 18aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 19aa7340388473c1676495a60e30dc6a48d318a489Ihab Awadimport java.util.ArrayList; 206059c9310c85986f38c22940470e5a0ff280806fSantos Cordonimport java.util.Collections; 2153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordonimport java.util.HashSet; 22aa7340388473c1676495a60e30dc6a48d318a489Ihab Awadimport java.util.List; 2353b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordonimport java.util.Set; 24aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 254d45d1cf58a2003378fd35912d6d73a00001bf06Tyler Gunnimport android.telecom.Conference; 264d45d1cf58a2003378fd35912d6d73a00001bf06Tyler Gunnimport android.telecom.Connection; 27aef7a4bc4f85149de427d7506ebe97753b2ca6c2Andrew Leeimport android.telecom.DisconnectCause; 28aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 2953b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordonimport com.android.internal.telephony.Call; 3053b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon 31aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad/** 32aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * Maintains a list of all the known GSM connections and implements GSM-specific conference 33aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * call functionality. 34aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad */ 353199aa7f8bcb48569eb8289abc055ba0f8496ba8Sailesh Nepalfinal class GsmConferenceController { 36079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad private static final int GSM_CONFERENCE_MAX_SIZE = 5; 37079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad 382093a451b17c26f4341e9565b65dcaa0e20bbd7dSailesh Nepal private final Connection.Listener mConnectionListener = new Connection.Listener() { 39af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen @Override 40af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen public void onStateChanged(Connection c, int state) { 41af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen recalculate(); 42af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen } 43aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 44af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen /** ${inheritDoc} */ 45af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen @Override 46aef7a4bc4f85149de427d7506ebe97753b2ca6c2Andrew Lee public void onDisconnected(Connection c, DisconnectCause disconnectCause) { 47af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen recalculate(); 48af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen } 49af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen 50af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen @Override 51af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen public void onDestroyed(Connection connection) { 52af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen remove((GsmConnection) connection); 53af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen } 54af4b3fd88b5412fbd644b1a4b9d1909c25681d4cNancy Chen }; 55aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 56aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad /** The known GSM connections. */ 57aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad private final List<GsmConnection> mGsmConnections = new ArrayList<>(); 58aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 5953b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon private final TelephonyConnectionService mConnectionService; 60aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 6153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon public GsmConferenceController(TelephonyConnectionService connectionService) { 6253b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mConnectionService = connectionService; 63aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad } 64aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 6553b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon /** The GSM conference connection object. */ 6653b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon private Conference mGsmConference; 67aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 6853b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon void add(GsmConnection connection) { 6953b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConnections.add(connection); 7053b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon connection.addConnectionListener(mConnectionListener); 7153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon recalculate(); 723199aa7f8bcb48569eb8289abc055ba0f8496ba8Sailesh Nepal } 733199aa7f8bcb48569eb8289abc055ba0f8496ba8Sailesh Nepal 7453b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon void remove(GsmConnection connection) { 7553b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon connection.removeConnectionListener(mConnectionListener); 7653b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConnections.remove(connection); 7753b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon recalculate(); 78aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad } 79aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 806059c9310c85986f38c22940470e5a0ff280806fSantos Cordon private void recalculate() { 816059c9310c85986f38c22940470e5a0ff280806fSantos Cordon recalculateConferenceable(); 8253b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon recalculateConference(); 836059c9310c85986f38c22940470e5a0ff280806fSantos Cordon } 846059c9310c85986f38c22940470e5a0ff280806fSantos Cordon 85079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad private boolean isFullConference(Conference conference) { 86079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad return conference.getConnections().size() >= GSM_CONFERENCE_MAX_SIZE; 87079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad } 88079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad 89079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad private boolean participatesInFullConference(Connection connection) { 90079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad return connection.getConference() != null && 91079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad isFullConference(connection.getConference()); 92079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad } 93079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad 94aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad /** 95aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad * Calculates the conference-capable state of all GSM connections in this connection service. 96aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad */ 976059c9310c85986f38c22940470e5a0ff280806fSantos Cordon private void recalculateConferenceable() { 986059c9310c85986f38c22940470e5a0ff280806fSantos Cordon Log.v(this, "recalculateConferenceable : %d", mGsmConnections.size()); 996059c9310c85986f38c22940470e5a0ff280806fSantos Cordon 1006059c9310c85986f38c22940470e5a0ff280806fSantos Cordon List<Connection> activeConnections = new ArrayList<>(mGsmConnections.size()); 1016059c9310c85986f38c22940470e5a0ff280806fSantos Cordon List<Connection> backgroundConnections = new ArrayList<>(mGsmConnections.size()); 1026059c9310c85986f38c22940470e5a0ff280806fSantos Cordon 1036059c9310c85986f38c22940470e5a0ff280806fSantos Cordon // Loop through and collect all calls which are active or holding 104aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad for (GsmConnection connection : mGsmConnections) { 105079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad Log.d(this, "recalc - %s %s", connection.getState(), connection); 106079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad 107079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad if (!participatesInFullConference(connection)) { 108079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad switch (connection.getState()) { 109079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad case Connection.STATE_ACTIVE: 1106059c9310c85986f38c22940470e5a0ff280806fSantos Cordon activeConnections.add(connection); 111079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad continue; 112079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad case Connection.STATE_HOLDING: 1136059c9310c85986f38c22940470e5a0ff280806fSantos Cordon backgroundConnections.add(connection); 114079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad continue; 1156059c9310c85986f38c22940470e5a0ff280806fSantos Cordon default: 1166059c9310c85986f38c22940470e5a0ff280806fSantos Cordon break; 117aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad } 118aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad } 119079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad 120079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad connection.setConferenceableConnections(Collections.<Connection>emptyList()); 1216059c9310c85986f38c22940470e5a0ff280806fSantos Cordon } 1226059c9310c85986f38c22940470e5a0ff280806fSantos Cordon 1236059c9310c85986f38c22940470e5a0ff280806fSantos Cordon Log.v(this, "active: %d, holding: %d", 1246059c9310c85986f38c22940470e5a0ff280806fSantos Cordon activeConnections.size(), backgroundConnections.size()); 1256059c9310c85986f38c22940470e5a0ff280806fSantos Cordon 1266059c9310c85986f38c22940470e5a0ff280806fSantos Cordon // Go through all the active connections and set the background connections as 1276059c9310c85986f38c22940470e5a0ff280806fSantos Cordon // conferenceable. 1286059c9310c85986f38c22940470e5a0ff280806fSantos Cordon for (Connection connection : activeConnections) { 1296059c9310c85986f38c22940470e5a0ff280806fSantos Cordon connection.setConferenceableConnections(backgroundConnections); 1306059c9310c85986f38c22940470e5a0ff280806fSantos Cordon } 131aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad 1326059c9310c85986f38c22940470e5a0ff280806fSantos Cordon // Go through all the background connections and set the active connections as 1336059c9310c85986f38c22940470e5a0ff280806fSantos Cordon // conferenceable. 1346059c9310c85986f38c22940470e5a0ff280806fSantos Cordon for (Connection connection : backgroundConnections) { 1356059c9310c85986f38c22940470e5a0ff280806fSantos Cordon connection.setConferenceableConnections(activeConnections); 136aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad } 1373197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad 1383197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad // Set the conference as conferenceable with all the connections 139079bd15c102a656e570aba53f30c73118e75ac95Ihab Awad if (mGsmConference != null && !isFullConference(mGsmConference)) { 1403197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad List<Connection> nonConferencedConnections = new ArrayList<>(mGsmConnections.size()); 1413197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad for (GsmConnection c : mGsmConnections) { 1423197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad if (c.getConference() == null) { 1433197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad nonConferencedConnections.add(c); 1443197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad } 1453197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad } 1463197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad mGsmConference.setConferenceableConnections(nonConferencedConnections); 1473197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad } 1483197ea821dcdee1c30549f850b99c2cfc7f668cbIhab Awad 14953b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon // TODO: Do not allow conferencing of already conferenced connections. 15053b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 15153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon 15253b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon private void recalculateConference() { 15353b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon Set<GsmConnection> conferencedConnections = new HashSet<>(); 15453b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon 15553b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon for (GsmConnection connection : mGsmConnections) { 15653b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon com.android.internal.telephony.Connection radioConnection = 15753b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon connection.getOriginalConnection(); 15853b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon 15953b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon if (radioConnection != null) { 16053b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon Call.State state = radioConnection.getState(); 16153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon Call call = radioConnection.getCall(); 16253b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon if ((state == Call.State.ACTIVE || state == Call.State.HOLDING) && 16353b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon (call != null && call.isMultiparty())) { 16453b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon conferencedConnections.add(connection); 16553b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 16653b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 16753b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 16853b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon 16953b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon Log.d(this, "Recalculate conference calls %s %s.", 17053b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConference, conferencedConnections); 17153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon 17253b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon if (conferencedConnections.size() < 2) { 17353b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon Log.d(this, "less than two conference calls!"); 17453b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon // No more connections are conferenced, destroy any existing conference. 17553b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon if (mGsmConference != null) { 17653b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon Log.d(this, "with a conference to destroy!"); 17753b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConference.destroy(); 17853b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConference = null; 17953b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 18053b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } else { 18153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon if (mGsmConference != null) { 18253b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon List<Connection> existingConnections = mGsmConference.getConnections(); 18353b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon // Remove any that no longer exist 18453b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon for (Connection connection : existingConnections) { 18553b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon if (!conferencedConnections.contains(connection)) { 18653b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConference.removeConnection(connection); 18753b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 18853b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 18953b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon 19053b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon // Add any new ones 19153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon for (Connection connection : conferencedConnections) { 19253b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon if (!existingConnections.contains(connection)) { 19353b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConference.addConnection(connection); 19453b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 19553b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 19653b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } else { 19753b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConference = new GsmConference(null); 19853b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon for (Connection connection : conferencedConnections) { 1991c25892dc5f0170441422f15242df8887b7903edSantos Cordon Log.d(this, "Adding a connection to a conference call: %s %s", 2001c25892dc5f0170441422f15242df8887b7903edSantos Cordon mGsmConference, connection); 20153b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mGsmConference.addConnection(connection); 20253b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 20353b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon mConnectionService.addConference(mGsmConference); 20453b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 2058a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee 2068a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee // Set the conference state to the same state as its child connections. 2078a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee Connection conferencedConnection = mGsmConference.getConnections().get(0); 2088a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee switch (conferencedConnection.getState()) { 2098a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee case Connection.STATE_ACTIVE: 2108a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee mGsmConference.setActive(); 2118a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee break; 2128a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee case Connection.STATE_HOLDING: 2138a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee mGsmConference.setOnHold(); 2148a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee break; 2158a76b6b8715188f00fff4c0e52d579f10cc53de3Andrew Lee } 21653b84fe2dc796ef172d7c0f4b9bdc177cdeb0c0fSantos Cordon } 217aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad } 218aa7340388473c1676495a60e30dc6a48d318a489Ihab Awad} 219