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

Commit 099052ef authored by Sanket Agarwal's avatar Sanket Agarwal
Browse files

Tackle changing phone numbers by relying on IDs wherever possible.

AG can rewrite numbers (specially with international numbers) or simply
with brackets or other annotation. We currently do not handle such cases
well since phone number match is not accurate. Instead of relying on the
numbers them selves, we rely on the capability of AG to tell the ID of
the call in question.

Bug: b/28719753
Change-Id: I97842188f174072caf7f03e18b0597ac1912be17
(cherry picked from commit bb3dc297c976f4fab23f93f9cae00cd495739ea4)
parent fda1e4d5
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;
    }