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

Commit 0bfb869d authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Tackle changing phone numbers by relying on IDs wherever possible."

parents fda1e4d5 099052ef
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.bluetooth.hfpclient.connserv;

import android.bluetooth.BluetoothHeadsetClientCall;
import android.net.Uri;
import android.telecom.PhoneAccount;

/* Matching a call to internal state requires understanding of the ph number and the state of the
 * remote itself. The best way to associate a call with remote is to use the Call IDs that are
 * passed by the HFP AG role. But consider the scenario when even before a call is notified to the
 * remote and it tries to get back to us -- we execute a full cycle of Call -> Hangup. In such case
 * we have no recourse but to use phone number as the key. This class implements the matching logic
 * for phone numbers & IDs. It identifies uniquely a {@link HfpClientConnection}.
 */
class ConnectionKey {
    /* Initialize with invalid values */
    public static final int INVALID_ID = -1;

    private final int mId;
    private final Uri mPhoneNumber;

    ConnectionKey(int id, Uri phoneNumber) {
        if (id == INVALID_ID && phoneNumber == null) {
            throw new IllegalStateException("invalid id and phone number");
        }
        mId = id;
        mPhoneNumber = phoneNumber;
    }

    public static ConnectionKey getKey(BluetoothHeadsetClientCall call) {
        if (call == null) {
            throw new IllegalStateException("call may not be null");
        }

        // IDs in the call start with *1*.
        int id = call.getId() > 0 ? call.getId() : INVALID_ID;
        Uri phoneNumberUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null);
        return new ConnectionKey(id, phoneNumberUri);
    }

    public int getId() {
        return mId;
    }

    public Uri getPhoneNumber() {
        return mPhoneNumber;
    }

    @Override
    public String toString() {
        return "Key " + getId() + " Phone number " + getPhoneNumber();
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof ConnectionKey)) {
            return false;
        }

        ConnectionKey candidate = (ConnectionKey) o;

        /* Match based on IDs */
        if (getId() != INVALID_ID && candidate.getId() != INVALID_ID) {
            return (getId() == candidate.getId());
        }

        /* Otherwise match has to be based on phone numbers */
        return (getPhoneNumber() != null && candidate.getPhoneNumber() != null &&
                getPhoneNumber().equals(candidate.getPhoneNumber()));
    }

    @Override
    public int hashCode() {
        // Since we may do partial match based on either ID or phone numbers hence put all the items
        // in same bucket.
        return 0;
    }
}
+37 −10
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import java.util.Map;

public class HfpClientConnectionService extends ConnectionService {
    private static final String TAG = "HfpClientConnService";
    private static final boolean DBG = true;

    public static final String HFP_SCHEME = "hfpc";

@@ -59,7 +60,8 @@ public class HfpClientConnectionService extends ConnectionService {
    private BluetoothHeadsetClient mHeadsetProfile;
    private TelecomManager mTelecomManager;

    private Map<Uri, HfpClientConnection> mConnections = new HashMap<>();
    private Map<ConnectionKey, HfpClientConnection> mConnections =
        new HashMap<ConnectionKey, HfpClientConnection>();
    private HfpClientConference mConference;

    private boolean mPendingAcceptCall;
@@ -195,11 +197,27 @@ public class HfpClientConnectionService extends ConnectionService {
        }
    }

    private void handleCall(BluetoothHeadsetClientCall call) {
        Log.d(TAG, "Got call " + call);
    // Find the connection specified by the key, also update the key with ID if present.
    private synchronized HfpClientConnection findConnectionAndUpdateKey(ConnectionKey key) {
        if (DBG) {
            Log.d(TAG, "findConnectionAndUpdateKey local key set " + mConnections.toString());
        }

        HfpClientConnection conn = mConnections.get(key);
        if (conn != null && key.getId() != ConnectionKey.INVALID_ID) {
            Log.d(TAG, "Updating key for " + key.getPhoneNumber() + " to " + key.getId());
            mConnections.remove(key);
            mConnections.put(key, conn);
        }
        return conn;
    }

    private void handleCall(BluetoothHeadsetClientCall call) {
        Uri number = Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null);
        HfpClientConnection connection = mConnections.get(number);
        Log.d(TAG, "Got call " + call.toString(true) + "/" + number);
        ConnectionKey incomingKey = ConnectionKey.getKey(call);
        HfpClientConnection connection = findConnectionAndUpdateKey(incomingKey);

        if (connection != null) {
            connection.handleCallChanged(call);
        }
@@ -230,7 +248,9 @@ public class HfpClientConnectionService extends ConnectionService {
            }
        } else if (call.getState() == BluetoothHeadsetClientCall.CALL_STATE_TERMINATED) {
            Log.d(TAG, "Removing number " + number);
            mConnections.remove(number);
            synchronized (this) {
                mConnections.remove(ConnectionKey.getKey(call));
            }
        }
        updateConferenceableConnections();
    }
@@ -251,8 +271,11 @@ public class HfpClientConnectionService extends ConnectionService {
        BluetoothHeadsetClientCall call =
            request.getExtras().getParcelable(
                TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
        Uri number = Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null);
        HfpClientConnection connection = mConnections.get(number);
        HfpClientConnection connection = null;

        synchronized (this) {
            connection = mConnections.get(ConnectionKey.getKey(call));
        }

        if (connection != null) {
            connection.onAdded();
@@ -302,7 +325,11 @@ public class HfpClientConnectionService extends ConnectionService {
            request.getExtras().getParcelable(
                TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
        Uri number = Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null);
        HfpClientConnection connection = mConnections.get(number);

        HfpClientConnection connection = null;
        synchronized (this) {
            connection = mConnections.get(ConnectionKey.getKey(call));
        }

        if (connection != null) {
            connection.onAdded();
@@ -419,12 +446,12 @@ public class HfpClientConnectionService extends ConnectionService {
        return mAdapter.getRemoteDevice(btAddr);
    }

    private HfpClientConnection buildConnection(
    private synchronized HfpClientConnection buildConnection(
            BluetoothDevice device, BluetoothHeadsetClientCall call, Uri number) {
        Log.d(TAG, "Creating connection on " + device + " for " + call + "/" + number);
        HfpClientConnection connection =
                new HfpClientConnection(this, device, mHeadsetProfile, call, number);
        mConnections.put(number, connection);
        mConnections.put(new ConnectionKey(ConnectionKey.INVALID_ID, number), connection);
        return connection;
    }