Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c22c1370 authored by Sanket Agarwal's avatar Sanket Agarwal Committed by android-build-merger
Browse files

Conference calling via HFP

am: edb38dfa

Change-Id: I66987c64796a88b861fd81c8f9729216a3419d91
parents cbcfc800 edb38dfa
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -350,7 +350,7 @@ public class HeadsetClientService extends ProfileService {
                Log.w(TAG, "service is null");
                return false;
            }
            return service.terminateCall(device, call.getUUID());
            return service.terminateCall(device, call != null ? call.getUUID() : null);
        }

        @Override
+24 −5
Original line number Diff line number Diff line
@@ -501,7 +501,7 @@ final class HeadsetClientStateMachine extends StateMachine {
    }

    private void acceptCall(int flag, boolean retry) {
        int action;
        int action = -1;

        if (DBG) {
            Log.d(TAG, "acceptCall: (" + flag + ")");
@@ -518,6 +518,9 @@ final class HeadsetClientStateMachine extends StateMachine {
            }
        }

        if (DBG) {
            Log.d(TAG, "Call to accept: " + c);
        }
        switch (c.getState()) {
            case BluetoothHeadsetClientCall.CALL_STATE_INCOMING:
                if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
@@ -556,12 +559,21 @@ final class HeadsetClientStateMachine extends StateMachine {
                    break;
                }

                // if active calls are present action must be selected
                if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
                // if active calls are present then we have the option to either terminate the
                // existing call or hold the existing call. We hold the other call by default.
                if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD ||
                    flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
                    if (DBG) {
                        Log.d(TAG, "Accepting call with accept and hold");
                    }
                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
                } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
                    if (DBG) {
                        Log.d(TAG, "Accepting call with accept and reject");
                    }
                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
                } else {
                    Log.e(TAG, "Aceept call with invalid flag: " + flag);
                    return;
                }
                break;
@@ -606,6 +618,9 @@ final class HeadsetClientStateMachine extends StateMachine {
                BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
                BluetoothHeadsetClientCall.CALL_STATE_HELD);
        if (c == null) {
            if (DBG) {
                Log.d(TAG, "No call to reject, returning.");
            }
            return;
        }

@@ -627,6 +642,9 @@ final class HeadsetClientStateMachine extends StateMachine {
                return;
        }

        if (DBG) {
            Log.d(TAG, "Reject call action " + action);
        }
        if (handleCallActionNative(action, 0)) {
            addQueuedAction(REJECT_CALL, action);
        } else {
@@ -837,6 +855,7 @@ final class HeadsetClientStateMachine extends StateMachine {
    }

    public void doQuit() {
        Log.d(TAG, "doQuit");
        quitNow();
    }

@@ -1059,6 +1078,7 @@ final class HeadsetClientStateMachine extends StateMachine {
                    }
                    break;
                default:
                    Log.w(TAG, "Message not handled " + message);
                    return NOT_HANDLED;
            }
            return retValue;
@@ -1076,7 +1096,7 @@ final class HeadsetClientStateMachine extends StateMachine {
                    break;

                case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED:
                    Log.w(TAG, "HFPClient Connected from Connecting state");
                    Log.d(TAG, "HFPClient Connected from Connecting state");

                    mPeerFeatures = peer_feat;
                    mChldFeatures = chld_feat;
@@ -1155,7 +1175,6 @@ final class HeadsetClientStateMachine extends StateMachine {
            if (DBG) {
                Log.d(TAG, "Enter Connected: " + getCurrentMessage().what);
            }

            mAudioWbs = false;
        }

+1 −1
Original line number Diff line number Diff line
@@ -113,7 +113,7 @@ public class HfpClientConnection extends Connection {
    }

    public boolean inConference() {
        return mAdded && mCurrentCall.isMultiParty() &&
        return mAdded && mCurrentCall != null && mCurrentCall.isMultiParty() &&
                getState() != Connection.STATE_DISCONNECTED;
    }

+52 −63
Original line number Diff line number Diff line
@@ -144,7 +144,8 @@ public class HfpClientConnectionService extends ConnectionService {
                    }
                }
            } else if (BluetoothHeadsetClient.ACTION_CALL_CHANGED.equals(action)) {
                // If we are not connected, then when we actually do get connected -- the calls should
                // If we are not connected, then when we actually do get connected --
                // the calls should
                // be added (see ACTION_CONNECTION_STATE_CHANGED intent above).
                handleCall((BluetoothHeadsetClientCall)
                        intent.getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL));
@@ -202,7 +203,8 @@ public class HfpClientConnectionService extends ConnectionService {
        // In order to make sure that the service is sticky (recovers from errors when HFP
        // connection is still active) and to stop it we need a special intent since stopService
        // only recreates it.
        if (intent.getBooleanExtra(HeadsetClientService.HFP_CLIENT_STOP_TAG, false)) {
        if (intent != null &&
            intent.getBooleanExtra(HeadsetClientService.HFP_CLIENT_STOP_TAG, false)) {
            // Stop the service.
            stopSelf();
            return 0;
@@ -267,6 +269,7 @@ public class HfpClientConnectionService extends ConnectionService {
                mConnections.remove(call.getUUID());
            }
        }

        updateConferenceableConnections();
    }

@@ -385,74 +388,60 @@ public class HfpClientConnectionService extends ConnectionService {
        }
    }

    // Updates any conferencable connections.
    private void updateConferenceableConnections() {
        Collection<HfpClientConnection> all = mConnections.values();

        List<Connection> held = new ArrayList<>();
        List<Connection> active = new ArrayList<>();
        List<Connection> group = new ArrayList<>();
        for (HfpClientConnection connection : all) {
            switch (connection.getState()) {
                case Connection.STATE_ACTIVE:
                    active.add(connection);
                    break;
                case Connection.STATE_HOLDING:
                    held.add(connection);
                    break;
                default:
                    break;
        boolean addConf = false;
        if (DBG) {
            Log.d(TAG, "Existing connections: " + mConnections + " existing conference " +
                mConference);
        }
            if (connection.inConference()) {
                group.add(connection);

        // If we have an existing conference call then loop through all connections and update any
        // connections that may have switched from conference -> non-conference.
        if (mConference != null) {
            for (Connection confConn : mConference.getConnections()) {
                if (!((HfpClientConnection) confConn).inConference()) {
                    if (DBG) {
                        Log.d(TAG, "Removing connection " + confConn + " from conference.");
                    }
                    mConference.removeConnection(confConn);
                }
        for (Connection connection : held) {
            connection.setConferenceableConnections(active);
            }
        for (Connection connection : active) {
            connection.setConferenceableConnections(held);
        }
        if (group.size() > 1 && mConference == null) {
            BluetoothDevice device = getDevice(getHandle());
            mConference = new HfpClientConference(getHandle(), device, mHeadsetProfile);
            if (group.get(0).getState() == Connection.STATE_ACTIVE) {
                mConference.setActive();
            } else {
                mConference.setOnHold();

        // If we have connections that are not already part of the conference then add them.
        // NOTE: addConnection takes care of duplicates (by mem addr) and the lifecycle of a
        // connection is maintained by the UUID.
        for (Connection otherConn : mConnections.values()) {
            if (((HfpClientConnection) otherConn).inConference()) {
                // If this is the first connection with conference, create the conference first.
                if (mConference == null) {
                    mConference = new HfpClientConference(getHandle(), mDevice, mHeadsetProfile);
                }
            for (Connection connection : group) {
                mConference.addConnection(connection);
                if (mConference.addConnection(otherConn)) {
                    if (DBG) {
                        Log.d(TAG, "Adding connection " + otherConn + " to conference.");
                    }
            addConference(mConference);
                    addConf = true;
                }
        if (mConference != null) {
            List<Connection> toRemove = new ArrayList<>();
            for (Connection connection : mConference.getConnections()) {
                if (!((HfpClientConnection) connection).inConference()) {
                    toRemove.add(connection);
            }
        }
            for (Connection connection : toRemove) {
                mConference.removeConnection(connection);

        // If we have no connections in the conference we should simply end it.
        if (mConference != null && mConference.getConnections().size() == 0) {
            if (DBG) {
                Log.d(TAG, "Conference has no connection, destroying");
            }
            if (mConference.getConnections().size() <= 1) {
            mConference.destroy();
            mConference = null;
            } else {
                List<Connection> notConferenced = new ArrayList<>();
                for (Connection connection : all) {
                    if (connection.getConference() == null &&
                            (connection.getState() == Connection.STATE_HOLDING ||
                             connection.getState() == Connection.STATE_ACTIVE)) {
                        if (((HfpClientConnection) connection).inConference()) {
                            mConference.addConnection(connection);
                        } else {
                            notConferenced.add(connection);
                        }
        }

        // If we have a valid conference and not previously added then add it.
        if (mConference != null && addConf) {
            if (DBG) {
                Log.d(TAG, "Adding conference to stack.");
            }
                mConference.setConferenceableConnections(notConferenced);
            }
            addConference(mConference);
        }
    }

@@ -460,8 +449,8 @@ public class HfpClientConnectionService extends ConnectionService {
        for (HfpClientConnection connection : mConnections.values()) {
            connection.onHfpDisconnected();
        }

        mConnections.clear();

        if (mConference != null) {
            mConference.destroy();
            mConference = null;