168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon/*
268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * Copyright (C) 2014 The Android Open Source Project
368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon *
468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * you may not use this file except in compliance with the License.
668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * You may obtain a copy of the License at
768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon *
868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon *      http://www.apache.org/licenses/LICENSE-2.0
968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon *
1068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * Unless required by applicable law or agreed to in writing, software
1168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
1268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * See the License for the specific language governing permissions and
1468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * limitations under the License.
1568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon */
1668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
1768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonpackage com.android.server.telecom;
1868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
1968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.app.Service;
2068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.bluetooth.BluetoothAdapter;
2168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.bluetooth.BluetoothHeadset;
2268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.bluetooth.BluetoothProfile;
2368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.bluetooth.IBluetoothHeadsetPhone;
2468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.content.BroadcastReceiver;
2568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.content.Context;
2668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.content.Intent;
2768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.content.IntentFilter;
2868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.net.Uri;
2968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.os.Handler;
3068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.os.IBinder;
3168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.os.Message;
3268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.os.RemoteException;
3333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordonimport android.telecom.CallState;
3468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.telecom.PhoneAccount;
3588a4a60284de8e1222488667b810b098c16d22acSantos Cordonimport android.telecom.PhoneCapabilities;
3668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.telephony.PhoneNumberUtils;
3768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.telephony.TelephonyManager;
3868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport android.text.TextUtils;
3968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
4068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport com.android.server.telecom.CallsManager.CallsManagerListener;
4168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
4233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordonimport java.util.Collection;
4333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordonimport java.util.HashMap;
4468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonimport java.util.List;
4533fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordonimport java.util.Map;
4668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
4768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon/**
4868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * Bluetooth headset manager for Telecom. This class shares the call state with the bluetooth device
4968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon * and accepts call-related commands to perform on behalf of the BT device.
5068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon */
5168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordonpublic final class BluetoothPhoneService extends Service {
5268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    /**
5368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * Request object for performing synchronous requests to the main thread.
5468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     */
5568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static class MainThreadRequest {
5668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Object result;
5768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        int param;
5868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
5968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        MainThreadRequest(int param) {
6068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            this.param = param;
6168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
6268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
6368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        void setResult(Object value) {
6468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            result = value;
6568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            synchronized (this) {
6668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                notifyAll();
6768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
6868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
6968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
7068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
7168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final String TAG = "BluetoothPhoneService";
7268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
7368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int MSG_ANSWER_CALL = 1;
7468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int MSG_HANGUP_CALL = 2;
7568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int MSG_SEND_DTMF = 3;
7668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int MSG_PROCESS_CHLD = 4;
7768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int MSG_GET_NETWORK_OPERATOR = 5;
7868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int MSG_LIST_CURRENT_CALLS = 6;
7968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int MSG_QUERY_PHONE_STATE = 7;
8068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int MSG_GET_SUBSCRIBER_NUMBER = 8;
8168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
8268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    // match up with bthf_call_state_t of bt_hf.h
8368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CALL_STATE_ACTIVE = 0;
8468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CALL_STATE_HELD = 1;
8568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CALL_STATE_DIALING = 2;
8668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CALL_STATE_ALERTING = 3;
8768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CALL_STATE_INCOMING = 4;
8868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CALL_STATE_WAITING = 5;
8968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CALL_STATE_IDLE = 6;
9068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
9168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    // match up with bthf_call_state_t of bt_hf.h
9268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    // Terminate all held or set UDUB("busy") to a waiting call
9368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CHLD_TYPE_RELEASEHELD = 0;
9468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    // Terminate all active calls and accepts a waiting/held call
9568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD = 1;
9668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    // Hold all active calls and accepts a waiting/held call
9768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CHLD_TYPE_HOLDACTIVE_ACCEPTHELD = 2;
9868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    // Add all held calls to a conference
9968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private static final int CHLD_TYPE_ADDHELDTOCONF = 3;
10068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
101a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon    private int mNumActiveCalls = 0;
102a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon    private int mNumHeldCalls = 0;
103a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon    private int mBluetoothCallState = CALL_STATE_IDLE;
104a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon    private String mRingingAddress = null;
105a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon    private int mRingingAddressType = 0;
106dfe06f1dbb7c13497f5033a3fa215b8a1a0cd517Santos Cordon    private Call mOldHeldCall = null;
107a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon
10868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    /**
10968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
11068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * bluetooth headset code uses to control call.
11168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     */
11268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private final IBluetoothHeadsetPhone.Stub mBinder = new IBluetoothHeadsetPhone.Stub() {
11368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
11468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public boolean answerCall() throws RemoteException {
11568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            enforceModifyPermission();
11668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.i(TAG, "BT - answering call");
11768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return sendSynchronousRequest(MSG_ANSWER_CALL);
11868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
11968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
12068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
12168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public boolean hangupCall() throws RemoteException {
12268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            enforceModifyPermission();
12368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.i(TAG, "BT - hanging up call");
12468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return sendSynchronousRequest(MSG_HANGUP_CALL);
12568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
12668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
12768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
12868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public boolean sendDtmf(int dtmf) throws RemoteException {
12968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            enforceModifyPermission();
13068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.i(TAG, "BT - sendDtmf %c", Log.DEBUG ? dtmf : '.');
13168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return sendSynchronousRequest(MSG_SEND_DTMF, dtmf);
13268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
13368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
13468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
13568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public String getNetworkOperator() throws RemoteException {
13668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.i(TAG, "getNetworkOperator");
13768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            enforceModifyPermission();
13868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return sendSynchronousRequest(MSG_GET_NETWORK_OPERATOR);
13968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
14068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
14168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
14268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public String getSubscriberNumber() throws RemoteException {
14368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.i(TAG, "getSubscriberNumber");
14468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            enforceModifyPermission();
14568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return sendSynchronousRequest(MSG_GET_SUBSCRIBER_NUMBER);
14668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
14768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
14868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
14968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public boolean listCurrentCalls() throws RemoteException {
15088a4a60284de8e1222488667b810b098c16d22acSantos Cordon            // only log if it is after we recently updated the headset state or else it can clog
15188a4a60284de8e1222488667b810b098c16d22acSantos Cordon            // the android log since this can be queried every second.
15288a4a60284de8e1222488667b810b098c16d22acSantos Cordon            boolean logQuery = mHeadsetUpdatedRecently;
15388a4a60284de8e1222488667b810b098c16d22acSantos Cordon            mHeadsetUpdatedRecently = false;
15488a4a60284de8e1222488667b810b098c16d22acSantos Cordon
15588a4a60284de8e1222488667b810b098c16d22acSantos Cordon            if (logQuery) {
15688a4a60284de8e1222488667b810b098c16d22acSantos Cordon                Log.i(TAG, "listcurrentCalls");
15788a4a60284de8e1222488667b810b098c16d22acSantos Cordon            }
15868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            enforceModifyPermission();
15988a4a60284de8e1222488667b810b098c16d22acSantos Cordon            return sendSynchronousRequest(MSG_LIST_CURRENT_CALLS, logQuery ? 1 : 0);
16068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
16168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
16268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
16368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public boolean queryPhoneState() throws RemoteException {
16468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.i(TAG, "queryPhoneState");
16568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            enforceModifyPermission();
16668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return sendSynchronousRequest(MSG_QUERY_PHONE_STATE);
16768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
16868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
16968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
17068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public boolean processChld(int chld) throws RemoteException {
17168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.i(TAG, "processChld %d", chld);
17268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            enforceModifyPermission();
17368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return sendSynchronousRequest(MSG_PROCESS_CHLD, chld);
17468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
17568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
17668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
17768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void updateBtHandsfreeAfterRadioTechnologyChange() throws RemoteException {
17868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.d(TAG, "RAT change");
17968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            // deprecated
18068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
18168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
18268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
18368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void cdmaSetSecondCallState(boolean state) throws RemoteException {
18468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.d(TAG, "cdma 1");
18568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            // deprecated
18668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
18768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
18868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
18968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void cdmaSwapSecondCallState() throws RemoteException {
19068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.d(TAG, "cdma 2");
19168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            // deprecated
19268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
19368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    };
19468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
19568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    /**
19668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * Main-thread handler for BT commands.  Since telecom logic runs on a single thread, commands
19768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * that are sent to it from the headset need to be moved over to the main thread before
19868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * executing. This handler exists for that reason.
19968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     */
20068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private final Handler mHandler = new Handler() {
20168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
20268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void handleMessage(Message msg) {
20368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            MainThreadRequest request = msg.obj instanceof MainThreadRequest ?
20468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    (MainThreadRequest) msg.obj : null;
20568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            CallsManager callsManager = getCallsManager();
20668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Call call = null;
20768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
20868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.d(TAG, "handleMessage(%d) w/ param %s",
20968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    msg.what, request == null ? null : request.param);
21068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
21168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            switch (msg.what) {
21268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                case MSG_ANSWER_CALL:
21368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    try {
21468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        call = callsManager.getRingingCall();
21568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        if (call != null) {
21668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            getCallsManager().answerCall(call, 0);
21768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        }
21868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    } finally {
21968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        request.setResult(call != null);
22068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    }
22168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    break;
22268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
22368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                case MSG_HANGUP_CALL:
22468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    try {
22568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        call = callsManager.getForegroundCall();
22668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        if (call != null) {
22768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            callsManager.disconnectCall(call);
22868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        }
22968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    } finally {
23068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        request.setResult(call != null);
23168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    }
23268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    break;
23368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
23468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                case MSG_SEND_DTMF:
23568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    try {
23668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        call = callsManager.getForegroundCall();
23768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        if (call != null) {
23868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            // TODO: Consider making this a queue instead of starting/stopping
23968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            // in quick succession.
24068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            callsManager.playDtmfTone(call, (char) request.param);
24168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            callsManager.stopDtmfTone(call);
24268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        }
24368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    } finally {
24468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        request.setResult(call != null);
24568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    }
24668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    break;
24768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
24868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                case MSG_PROCESS_CHLD:
24968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    Boolean result = false;
25068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    try {
25168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        result = processChld(request.param);
25268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    } finally {
25368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        request.setResult(result);
25468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    }
25568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    break;
25668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
25768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                case MSG_GET_SUBSCRIBER_NUMBER:
25868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    String address = null;
25968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    try {
26068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        PhoneAccount account = getBestPhoneAccount();
26168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        if (account != null) {
26268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            Uri addressUri = account.getAddress();
26368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            if (addressUri != null) {
26468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                                address = addressUri.getSchemeSpecificPart();
26568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            }
26668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        }
26768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
26868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        if (TextUtils.isEmpty(address)) {
26968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            address = TelephonyManager.from(BluetoothPhoneService.this)
27068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                                    .getLine1Number();
27168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        }
27268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    } finally {
27368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        request.setResult(address);
27468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    }
27568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    break;
27668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
27768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                case MSG_GET_NETWORK_OPERATOR:
27868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    String label = null;
27968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    try {
28068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        PhoneAccount account = getBestPhoneAccount();
28168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        if (account != null) {
28268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            label = account.getLabel().toString();
28368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        } else {
28468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            // Finally, just get the network name from telephony.
28568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            label = TelephonyManager.from(BluetoothPhoneService.this)
28668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                                    .getNetworkOperatorName();
28768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        }
28868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    } finally {
28968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        request.setResult(label);
29068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    }
29168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    break;
29268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
29368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                case MSG_LIST_CURRENT_CALLS:
29433fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                    try {
29588a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        sendListOfCalls(request.param == 1);
29633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                    } finally {
29733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                        request.setResult(true);
29833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                    }
29968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    break;
30068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
30168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                case MSG_QUERY_PHONE_STATE:
30268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    try {
303cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn                        updateHeadsetWithCallState(true /* force */);
30468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    } finally {
30568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        if (request != null) {
30668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                            request.setResult(true);
30768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                        }
30868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    }
30968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    break;
31068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
31168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
31268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    };
31368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
31468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    /**
31568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * Listens to call changes from the CallsManager and calls into methods to update the bluetooth
31668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * headset with the new states.
31768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     */
31868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private CallsManagerListener mCallsManagerListener = new CallsManagerListenerBase() {
31968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
32068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void onCallAdded(Call call) {
321cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn            updateHeadsetWithCallState(false /* force */);
32268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
32368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
32468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
32568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void onCallRemoved(Call call) {
32633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            mClccIndexMap.remove(call);
327cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn            updateHeadsetWithCallState(false /* force */);
32868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
32968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
33068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
33168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void onCallStateChanged(Call call, int oldState, int newState) {
332df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // If a call is being put on hold because of a new connecting call, ignore the
333df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // CONNECTING since the BT state update needs to send out the numHeld = 1 + dialing
334df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // state atomically.
335df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // When the call later transitions to DIALING/DISCONNECTED we will then send out the
336df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // aggregated update.
337df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            if (oldState == CallState.ACTIVE && newState == CallState.ON_HOLD) {
338df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                for (Call otherCall : CallsManager.getInstance().getCalls()) {
339df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                    if (otherCall.getState() == CallState.CONNECTING) {
340df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        return;
341df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                    }
342df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                }
343df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            }
344df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee
345df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // To have an active call and another dialing at the same time is an invalid BT
346df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // state. We can assume that the active call will be automatically held which will
347df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // send another update at which point we will be in the right state.
348df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            if (CallsManager.getInstance().getActiveCall() != null
349df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                    && oldState == CallState.CONNECTING && newState == CallState.DIALING) {
350df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                return;
351df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            }
352cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn            updateHeadsetWithCallState(false /* force */);
35368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
35468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
35568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
35668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
357df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // The BluetoothPhoneService does not need to respond to changes in foreground calls,
358df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            // which are always accompanied by call state changes anyway.
35968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
36068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
36168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
36268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void onIsConferencedChanged(Call call) {
363df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            /*
364df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * Filter certain onIsConferencedChanged callbacks. Unfortunately this needs to be done
365df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * because conference change events are not atomic and multiple callbacks get fired
366df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * when two calls are conferenced together. This confuses updateHeadsetWithCallState
367df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * if it runs in the middle of two calls being conferenced and can cause spurious and
368df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * incorrect headset state updates. One of the scenarios is described below for CDMA
369df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * conference calls.
370df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             *
371df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * 1) Call 1 and Call 2 are being merged into conference Call 3.
372df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * 2) Call 1 has its parent set to Call 3, but Call 2 does not have a parent yet.
373df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * 3) updateHeadsetWithCallState now thinks that there are two active calls (Call 2 and
374df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             * Call 3) when there is actually only one active call (Call 3).
375df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee             */
376df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            if (call.getParentCall() != null) {
377df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // If this call is newly conferenced, ignore the callback. We only care about the
378df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // one sent for the parent conference call.
379df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                Log.d(this, "Ignoring onIsConferenceChanged from child call with new parent");
380df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                return;
381df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            }
382df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            if (call.getChildCalls().size() == 1) {
383df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // If this is a parent call with only one child, ignore the callback as well since
384df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // the minimum number of child calls to start a conference call is 2. We expect
385df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // this to be called again when the parent call has another child call added.
386df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                Log.d(this, "Ignoring onIsConferenceChanged from parent with only one child call");
387df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                return;
388df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            }
389cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn            updateHeadsetWithCallState(false /* force */);
39068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
39168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    };
39268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
39368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    /**
39468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * Listens to connections and disconnections of bluetooth headsets.  We need to save the current
39568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * bluetooth headset so that we know where to send call updates.
39668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     */
39768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private BluetoothProfile.ServiceListener mProfileListener =
39868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            new BluetoothProfile.ServiceListener() {
39968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
40068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void onServiceConnected(int profile, BluetoothProfile proxy) {
40168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            mBluetoothHeadset = (BluetoothHeadset) proxy;
40268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
40368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
40468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
40568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void onServiceDisconnected(int profile) {
40668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            mBluetoothHeadset = null;
40768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
40868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    };
40968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
41068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    /**
41168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * Receives events for global state changes of the bluetooth adapter.
41268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     */
41368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private final BroadcastReceiver mBluetoothAdapterReceiver = new BroadcastReceiver() {
41468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        @Override
41568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        public void onReceive(Context context, Intent intent) {
41668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
41768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.d(TAG, "Bluetooth Adapter state: %d", state);
41868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            if (state == BluetoothAdapter.STATE_ON) {
41968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                mHandler.sendEmptyMessage(MSG_QUERY_PHONE_STATE);
42068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
42168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
42268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    };
42368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
42468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private BluetoothAdapter mBluetoothAdapter;
42568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private BluetoothHeadset mBluetoothHeadset;
42668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
42733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    // A map from Calls to indexes used to identify calls for CLCC (C* List Current Calls).
42833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    private Map<Call, Integer> mClccIndexMap = new HashMap<>();
42933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
43088a4a60284de8e1222488667b810b098c16d22acSantos Cordon    private boolean mHeadsetUpdatedRecently = false;
43188a4a60284de8e1222488667b810b098c16d22acSantos Cordon
43268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    public BluetoothPhoneService() {
43368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Log.v(TAG, "Constructor");
43468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
43568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
43668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    public static final void start(Context context) {
43768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (BluetoothAdapter.getDefaultAdapter() != null) {
43868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            context.startService(new Intent(context, BluetoothPhoneService.class));
43968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
44068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
44168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
44268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    @Override
44368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    public IBinder onBind(Intent intent) {
44468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Log.d(TAG, "Binding service");
44568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        return mBinder;
44668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
44768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
44868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    @Override
44968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    public void onCreate() {
45068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Log.d(TAG, "onCreate");
45168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
45268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
45368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (mBluetoothAdapter == null) {
45468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            Log.d(TAG, "BluetoothPhoneService shutting down, no BT Adapter found.");
45568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return;
45668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
45768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        mBluetoothAdapter.getProfileProxy(this, mProfileListener, BluetoothProfile.HEADSET);
45868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
45968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
46068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        registerReceiver(mBluetoothAdapterReceiver, intentFilter);
46168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
46268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        CallsManager.getInstance().addListener(mCallsManagerListener);
463cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn        updateHeadsetWithCallState(false /* force */);
46468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
46568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
46668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    @Override
46768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    public void onDestroy() {
46868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Log.d(TAG, "onDestroy");
46968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        CallsManager.getInstance().removeListener(mCallsManagerListener);
47068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        super.onDestroy();
47168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
47268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
47368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private boolean processChld(int chld) {
47468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        CallsManager callsManager = CallsManager.getInstance();
47568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Call activeCall = callsManager.getActiveCall();
47668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Call ringingCall = callsManager.getRingingCall();
47768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Call heldCall = callsManager.getHeldCall();
47868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
47966fe882a6437de1010af0dc8cd7350f81f2e028aSantos Cordon        // TODO: Keeping as Log.i for now.  Move to Log.d after L release if BT proves stable.
48066fe882a6437de1010af0dc8cd7350f81f2e028aSantos Cordon        Log.i(TAG, "Active: %s\nRinging: %s\nHeld: %s", activeCall, ringingCall, heldCall);
48188a4a60284de8e1222488667b810b098c16d22acSantos Cordon
48268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (chld == CHLD_TYPE_RELEASEHELD) {
48368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            if (ringingCall != null) {
48468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                callsManager.rejectCall(ringingCall, false, null);
48568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                return true;
48668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            } else if (heldCall != null) {
48768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                callsManager.disconnectCall(heldCall);
48868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                return true;
48968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
49068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        } else if (chld == CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD) {
49168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            if (activeCall != null) {
49268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                callsManager.disconnectCall(activeCall);
49368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                if (ringingCall != null) {
49468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    callsManager.answerCall(ringingCall, 0);
49568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                } else if (heldCall != null) {
49668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    callsManager.unholdCall(heldCall);
49768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                }
49868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                return true;
49968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
50068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        } else if (chld == CHLD_TYPE_HOLDACTIVE_ACCEPTHELD) {
50188a4a60284de8e1222488667b810b098c16d22acSantos Cordon            if (activeCall != null && activeCall.can(PhoneCapabilities.SWAP_CONFERENCE)) {
50288a4a60284de8e1222488667b810b098c16d22acSantos Cordon                activeCall.swapConference();
50388a4a60284de8e1222488667b810b098c16d22acSantos Cordon                return true;
50488a4a60284de8e1222488667b810b098c16d22acSantos Cordon            } else if (ringingCall != null) {
50568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                callsManager.answerCall(ringingCall, 0);
50668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                return true;
50768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            } else if (heldCall != null) {
50868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                // CallsManager will hold any active calls when unhold() is called on a
50968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                // currently-held call.
51068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                callsManager.unholdCall(heldCall);
51168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                return true;
51288a4a60284de8e1222488667b810b098c16d22acSantos Cordon            } else if (activeCall != null && activeCall.can(PhoneCapabilities.HOLD)) {
51368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                callsManager.holdCall(activeCall);
51468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                return true;
51568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
51668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        } else if (chld == CHLD_TYPE_ADDHELDTOCONF) {
51768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            if (activeCall != null) {
51866fe882a6437de1010af0dc8cd7350f81f2e028aSantos Cordon                if (activeCall.can(PhoneCapabilities.MERGE_CONFERENCE)) {
51988a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    activeCall.mergeConference();
52068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    return true;
52188a4a60284de8e1222488667b810b098c16d22acSantos Cordon                } else {
52288a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    List<Call> conferenceable = activeCall.getConferenceableCalls();
52388a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    if (!conferenceable.isEmpty()) {
52488a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        callsManager.conference(activeCall, conferenceable.get(0));
52588a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        return true;
52688a4a60284de8e1222488667b810b098c16d22acSantos Cordon                   }
52768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                }
52868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
52968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
53068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        return false;
53168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
53268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
53368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private void enforceModifyPermission() {
53468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
53568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
53668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
53768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private <T> T sendSynchronousRequest(int message) {
53868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        return sendSynchronousRequest(message, 0);
53968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
54068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
54168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private <T> T sendSynchronousRequest(int message, int param) {
54268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        MainThreadRequest request = new MainThreadRequest(param);
54368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        mHandler.obtainMessage(message, request).sendToTarget();
54468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        synchronized (request) {
54568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            while (request.result == null) {
54668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                try {
54768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    request.wait();
54868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                } catch (InterruptedException e) {
54968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    // Do nothing, go back and wait until the request is complete.
55068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                }
55168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
55268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
55368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (request.result != null) {
55468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            @SuppressWarnings("unchecked")
55568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            T retval = (T) request.result;
55668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            return retval;
55768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
55868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        return null;
55968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
56068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
56188a4a60284de8e1222488667b810b098c16d22acSantos Cordon    private void sendListOfCalls(boolean shouldLog) {
56233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        Collection<Call> mCalls = getCallsManager().getCalls();
56333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        for (Call call : mCalls) {
56433fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            // We don't send the parent conference call to the bluetooth device.
56533fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            if (!call.isConference()) {
56688a4a60284de8e1222488667b810b098c16d22acSantos Cordon                sendClccForCall(call, shouldLog);
56733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            }
56833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        }
56933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        sendClccEndMarker();
57033fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    }
57133fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
57233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    /**
57333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon     * Sends a single clcc (C* List Current Calls) event for the specified call.
57433fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon     */
57588a4a60284de8e1222488667b810b098c16d22acSantos Cordon    private void sendClccForCall(Call call, boolean shouldLog) {
57633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        boolean isForeground = getCallsManager().getForegroundCall() == call;
57733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        int state = convertCallState(call.getState(), isForeground);
57888a4a60284de8e1222488667b810b098c16d22acSantos Cordon        boolean isPartOfConference = false;
57905a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen
58005a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen        if (state == CALL_STATE_IDLE) {
58105a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen            return;
58205a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen        }
58305a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen
58488a4a60284de8e1222488667b810b098c16d22acSantos Cordon        Call conferenceCall = call.getParentCall();
58588a4a60284de8e1222488667b810b098c16d22acSantos Cordon        if (conferenceCall != null) {
58688a4a60284de8e1222488667b810b098c16d22acSantos Cordon            isPartOfConference = true;
58788a4a60284de8e1222488667b810b098c16d22acSantos Cordon
58888a4a60284de8e1222488667b810b098c16d22acSantos Cordon            // Run some alternative states for Conference-level merge/swap support.
58988a4a60284de8e1222488667b810b098c16d22acSantos Cordon            // Basically, if call supports swapping or merging at the conference-level, then we need
59088a4a60284de8e1222488667b810b098c16d22acSantos Cordon            // to expose the calls as having distinct states (ACTIVE vs HOLD) or the functionality
59188a4a60284de8e1222488667b810b098c16d22acSantos Cordon            // won't show up on the bluetooth device.
59288a4a60284de8e1222488667b810b098c16d22acSantos Cordon
59388a4a60284de8e1222488667b810b098c16d22acSantos Cordon            // Before doing any special logic, ensure that we are dealing with an ACTIVE call and
59488a4a60284de8e1222488667b810b098c16d22acSantos Cordon            // that the conference itself has a notion of the current "active" child call.
59588a4a60284de8e1222488667b810b098c16d22acSantos Cordon            Call activeChild = conferenceCall.getConferenceLevelActiveCall();
59688a4a60284de8e1222488667b810b098c16d22acSantos Cordon            if (state == CALL_STATE_ACTIVE && activeChild != null) {
59788a4a60284de8e1222488667b810b098c16d22acSantos Cordon                // Reevaluate state if we can MERGE or if we can SWAP without previously having
59888a4a60284de8e1222488667b810b098c16d22acSantos Cordon                // MERGED.
59988a4a60284de8e1222488667b810b098c16d22acSantos Cordon                boolean shouldReevaluateState =
60088a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        conferenceCall.can(PhoneCapabilities.MERGE_CONFERENCE) ||
60188a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        (conferenceCall.can(PhoneCapabilities.SWAP_CONFERENCE) &&
60288a4a60284de8e1222488667b810b098c16d22acSantos Cordon                         !conferenceCall.wasConferencePreviouslyMerged());
60388a4a60284de8e1222488667b810b098c16d22acSantos Cordon
60488a4a60284de8e1222488667b810b098c16d22acSantos Cordon                if (shouldReevaluateState) {
60588a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    isPartOfConference = false;
60688a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    if (call == activeChild) {
60788a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        state = CALL_STATE_ACTIVE;
60888a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    } else {
60988a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        // At this point we know there is an "active" child and we know that it is
61088a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        // not this call, so set it to HELD instead.
61188a4a60284de8e1222488667b810b098c16d22acSantos Cordon                        state = CALL_STATE_HELD;
61288a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    }
61388a4a60284de8e1222488667b810b098c16d22acSantos Cordon                }
61488a4a60284de8e1222488667b810b098c16d22acSantos Cordon            }
61588a4a60284de8e1222488667b810b098c16d22acSantos Cordon        }
61688a4a60284de8e1222488667b810b098c16d22acSantos Cordon
61705a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen        int index = getIndexForCall(call);
61805a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen        int direction = call.isIncoming() ? 1 : 0;
619fb70e1efbcf173a995e9bb9b312713f8f554e307Yorke Lee        final Uri addressUri;
620fb70e1efbcf173a995e9bb9b312713f8f554e307Yorke Lee        if (call.getGatewayInfo() != null) {
621fb70e1efbcf173a995e9bb9b312713f8f554e307Yorke Lee            addressUri = call.getGatewayInfo().getOriginalAddress();
622fb70e1efbcf173a995e9bb9b312713f8f554e307Yorke Lee        } else {
623fb70e1efbcf173a995e9bb9b312713f8f554e307Yorke Lee            addressUri = call.getHandle();
624fb70e1efbcf173a995e9bb9b312713f8f554e307Yorke Lee        }
62533fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        String address = addressUri == null ? null : addressUri.getSchemeSpecificPart();
62633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        int addressType = address == null ? -1 : PhoneNumberUtils.toaFromString(address);
62733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
62888a4a60284de8e1222488667b810b098c16d22acSantos Cordon        if (shouldLog) {
62988a4a60284de8e1222488667b810b098c16d22acSantos Cordon            Log.i(this, "sending clcc for call %d, %d, %d, %b, %s, %d",
63088a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    index, direction, state, isPartOfConference, Log.piiHandle(address),
63188a4a60284de8e1222488667b810b098c16d22acSantos Cordon                    addressType);
63288a4a60284de8e1222488667b810b098c16d22acSantos Cordon        }
633e241cd6f340fedb82e1aa76d35bbcbca31fe423cYorke Lee
634e241cd6f340fedb82e1aa76d35bbcbca31fe423cYorke Lee        if (mBluetoothHeadset != null) {
635e241cd6f340fedb82e1aa76d35bbcbca31fe423cYorke Lee            mBluetoothHeadset.clccResponse(
636e241cd6f340fedb82e1aa76d35bbcbca31fe423cYorke Lee                    index, direction, state, 0, isPartOfConference, address, addressType);
637e241cd6f340fedb82e1aa76d35bbcbca31fe423cYorke Lee        }
63833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    }
63933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
64033fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    private void sendClccEndMarker() {
64133fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        // End marker is recognized with an index value of 0. All other parameters are ignored.
642e241cd6f340fedb82e1aa76d35bbcbca31fe423cYorke Lee        if (mBluetoothHeadset != null) {
643e241cd6f340fedb82e1aa76d35bbcbca31fe423cYorke Lee            mBluetoothHeadset.clccResponse(0 /* index */, 0, 0, 0, false, null, 0);
644e241cd6f340fedb82e1aa76d35bbcbca31fe423cYorke Lee        }
64533fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    }
64633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
64733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    /**
64833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon     * Returns the caches index for the specified call.  If no such index exists, then an index is
64933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon     * given (smallest number starting from 1 that isn't already taken).
65033fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon     */
65133fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    private int getIndexForCall(Call call) {
65233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        if (mClccIndexMap.containsKey(call)) {
65333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            return mClccIndexMap.get(call);
65433fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        }
65533fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
65633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        int i = 1;  // Indexes for bluetooth clcc are 1-based.
65733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        while (mClccIndexMap.containsValue(i)) {
65833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            i++;
65933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        }
66033fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
66133fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        // NOTE: Indexes are removed in {@link #onCallRemoved}.
66233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        mClccIndexMap.put(call, i);
66333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        return i;
66433fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    }
66533fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
666cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn    /**
667cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn     * Sends an update of the current call state to the current Headset.
668cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn     *
669cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn     * @param force {@code true} if the headset state should be sent regardless if no changes to the
670cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn     *      state have occurred, {@code false} if the state should only be sent if the state has
671cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn     *      changed.
672cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn     */
673cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn    private void updateHeadsetWithCallState(boolean force) {
67468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        CallsManager callsManager = getCallsManager();
67568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Call activeCall = callsManager.getActiveCall();
67668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Call ringingCall = callsManager.getRingingCall();
67768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Call heldCall = callsManager.getHeldCall();
67868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
67968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        int bluetoothCallState = getBluetoothCallStateForUpdate();
68068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
68168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        String ringingAddress = null;
68268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        int ringingAddressType = 128;
68368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (ringingCall != null) {
68468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            ringingAddress = ringingCall.getHandle().getSchemeSpecificPart();
68568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            if (ringingAddress != null) {
68668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                ringingAddressType = PhoneNumberUtils.toaFromString(ringingAddress);
68768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            }
68868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
68968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (ringingAddress == null) {
69068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            ringingAddress = "";
69168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
69268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
693a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon        int numActiveCalls = activeCall == null ? 0 : 1;
694dfe06f1dbb7c13497f5033a3fa215b8a1a0cd517Santos Cordon        int numHeldCalls = callsManager.getNumHeldCalls();
695a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon
69688a4a60284de8e1222488667b810b098c16d22acSantos Cordon        // For conference calls which support swapping the active call within the conference
69788a4a60284de8e1222488667b810b098c16d22acSantos Cordon        // (namely CDMA calls) we need to expose that as a held call in order for the BT device
69888a4a60284de8e1222488667b810b098c16d22acSantos Cordon        // to show "swap" and "merge" functionality.
699df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee        boolean ignoreHeldCallChange = false;
70088a4a60284de8e1222488667b810b098c16d22acSantos Cordon        if (activeCall != null && activeCall.isConference()) {
70188a4a60284de8e1222488667b810b098c16d22acSantos Cordon            if (activeCall.can(PhoneCapabilities.SWAP_CONFERENCE)) {
70288a4a60284de8e1222488667b810b098c16d22acSantos Cordon                // Indicate that BT device should show SWAP command by indicating that there is a
70388a4a60284de8e1222488667b810b098c16d22acSantos Cordon                // call on hold, but only if the conference wasn't previously merged.
70488a4a60284de8e1222488667b810b098c16d22acSantos Cordon                numHeldCalls = activeCall.wasConferencePreviouslyMerged() ? 0 : 1;
70588a4a60284de8e1222488667b810b098c16d22acSantos Cordon            } else if (activeCall.can(PhoneCapabilities.MERGE_CONFERENCE)) {
70688a4a60284de8e1222488667b810b098c16d22acSantos Cordon                numHeldCalls = 1;  // Merge is available, so expose via numHeldCalls.
70788a4a60284de8e1222488667b810b098c16d22acSantos Cordon            }
708df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee
709df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            for (Call childCall : activeCall.getChildCalls()) {
710df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // Held call has changed due to it being combined into a CDMA conference. Keep
711df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // track of this and ignore any future update since it doesn't really count as
712df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // a call change.
713df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                if (mOldHeldCall == childCall) {
714df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                    ignoreHeldCallChange = true;
715df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                    break;
716df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                }
717df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee            }
71888a4a60284de8e1222488667b810b098c16d22acSantos Cordon        }
71988a4a60284de8e1222488667b810b098c16d22acSantos Cordon
720a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon        if (mBluetoothHeadset != null &&
721a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                (numActiveCalls != mNumActiveCalls ||
722a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                 numHeldCalls != mNumHeldCalls ||
723a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                 bluetoothCallState != mBluetoothCallState ||
724a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                 !TextUtils.equals(ringingAddress, mRingingAddress) ||
725cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn                 ringingAddressType != mRingingAddressType ||
726df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                 (heldCall != mOldHeldCall && !ignoreHeldCallChange) ||
727cd685e56453b2e1feb118b560a1caa1ef40039d6Tyler Gunn                 force)) {
728a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon
729a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            // If the call is transitioning into the alerting state, send DIALING first.
730a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            // Some devices expect to see a DIALING state prior to seeing an ALERTING state
731a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            // so we need to send it first.
732a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            boolean sendDialingFirst = mBluetoothCallState != bluetoothCallState &&
733a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    bluetoothCallState == CALL_STATE_ALERTING;
734a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon
735dfe06f1dbb7c13497f5033a3fa215b8a1a0cd517Santos Cordon            mOldHeldCall = heldCall;
736a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            mNumActiveCalls = numActiveCalls;
737a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            mNumHeldCalls = numHeldCalls;
738a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            mBluetoothCallState = bluetoothCallState;
739a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            mRingingAddress = ringingAddress;
740a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            mRingingAddressType = ringingAddressType;
741a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon
742a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            if (sendDialingFirst) {
743df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                // Log in full to make logs easier to debug.
744df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                Log.i(TAG, "updateHeadsetWithCallState " +
745df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        "numActive %s, " +
746df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        "numHeld %s, " +
747df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        "callState %s, " +
748df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        "ringing number %s, " +
749df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        "ringing type %s",
750df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        mNumActiveCalls,
751df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        mNumHeldCalls,
752df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        CALL_STATE_DIALING,
753df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        Log.pii(mRingingAddress),
754df441ed20bec7f2ebe319afc57f52df5f3996920Yorke Lee                        mRingingAddressType);
755a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                mBluetoothHeadset.phoneStateChanged(
756a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                        mNumActiveCalls,
757a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                        mNumHeldCalls,
758a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                        CALL_STATE_DIALING,
759a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                        mRingingAddress,
760a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                        mRingingAddressType);
761a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            }
76268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
763a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon            Log.i(TAG, "updateHeadsetWithCallState " +
764a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    "numActive %s, " +
765a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    "numHeld %s, " +
766a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    "callState %s, " +
767a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    "ringing number %s, " +
768a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    "ringing type %s",
769a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mNumActiveCalls,
770a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mNumHeldCalls,
771a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mBluetoothCallState,
772a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    Log.pii(mRingingAddress),
773a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mRingingAddressType);
77468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
77568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            mBluetoothHeadset.phoneStateChanged(
776a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mNumActiveCalls,
777a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mNumHeldCalls,
778a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mBluetoothCallState,
779a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mRingingAddress,
780a0b4612909c6046315a11072d245e4ff0b9e784eSantos Cordon                    mRingingAddressType);
78188a4a60284de8e1222488667b810b098c16d22acSantos Cordon
78288a4a60284de8e1222488667b810b098c16d22acSantos Cordon            mHeadsetUpdatedRecently = true;
78368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
78468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
78568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
78668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private int getBluetoothCallStateForUpdate() {
78768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        CallsManager callsManager = getCallsManager();
78868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Call ringingCall = callsManager.getRingingCall();
78905a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen        Call dialingCall = callsManager.getDialingCall();
79068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
79168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        //
79268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        // !! WARNING !!
79368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        // You will note that CALL_STATE_WAITING, CALL_STATE_HELD, and CALL_STATE_ACTIVE are not
79468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        // used in this version of the call state mappings.  This is on purpose.
79568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        // phone_state_change() in btif_hf.c is not written to handle these states. Only with the
79668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        // listCalls*() method are WAITING and ACTIVE used.
79768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        // Using the unsupported states here caused problems with inconsistent state in some
79868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        // bluetooth devices (like not getting out of ringing state after answering a call).
79968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        //
80068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        int bluetoothCallState = CALL_STATE_IDLE;
80168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (ringingCall != null) {
80268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            bluetoothCallState = CALL_STATE_INCOMING;
80305a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen        } else if (dialingCall != null) {
80468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            bluetoothCallState = CALL_STATE_ALERTING;
80568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
80668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        return bluetoothCallState;
80768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
80868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
80933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    private int convertCallState(int callState, boolean isForegroundCall) {
81033fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        switch (callState) {
81133fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            case CallState.NEW:
81233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            case CallState.ABORTED:
81333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            case CallState.DISCONNECTED:
81405a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen            case CallState.CONNECTING:
81505a9e40ba1edc7b4597163f73503e2e92b746208Nancy Chen            case CallState.PRE_DIAL_WAIT:
81633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                return CALL_STATE_IDLE;
81733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
81833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            case CallState.ACTIVE:
81933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                return CALL_STATE_ACTIVE;
82033fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
82133fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            case CallState.DIALING:
82233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                // Yes, this is correctly returning ALERTING.
82333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                // "Dialing" for BT means that we have sent information to the service provider
82433fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                // to place the call but there is no confirmation that the call is going through.
82533fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                // When there finally is confirmation, the ringback is played which is referred to
82633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                // as an "alert" tone, thus, ALERTING.
82733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                // TODO: We should consider using the ALERTING terms in Telecom because that
82833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                // seems to be more industry-standard.
82933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                return CALL_STATE_ALERTING;
83033fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
83133fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            case CallState.ON_HOLD:
83233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                return CALL_STATE_HELD;
83333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
83433fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon            case CallState.RINGING:
83533fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                if (isForegroundCall) {
83633fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                    return CALL_STATE_INCOMING;
83733fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                } else {
83833fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                    return CALL_STATE_WAITING;
83933fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon                }
84033fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        }
84133fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon        return CALL_STATE_IDLE;
84233fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon    }
84333fff4905fb4e977cb982fbc89d5d0c52a92b55cSantos Cordon
84468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private CallsManager getCallsManager() {
84568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        return CallsManager.getInstance();
84668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
84768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
84868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    /**
84968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * Returns the best phone account to use for the given state of all calls.
85068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * First, tries to return the phone account for the foreground call, second the default
85168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     * phone account for PhoneAccount.SCHEME_TEL.
85268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon     */
85368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    private PhoneAccount getBestPhoneAccount() {
85468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        TelecomApp app = (TelecomApp) getApplication();
85568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        PhoneAccountRegistrar registry = app.getPhoneAccountRegistrar();
85668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        Call call = getCallsManager().getForegroundCall();
85768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
85868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        PhoneAccount account = null;
85968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (call != null) {
86068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            // First try to get the network name of the foreground call.
86168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            account = registry.getPhoneAccount(call.getTargetPhoneAccount());
86268d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
86368d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon
86468d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        if (account == null) {
86568d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            // Second, Try to get the label for the default Phone Account.
86668d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon            account = registry.getPhoneAccount(
86768d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon                    registry.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL));
86868d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        }
86968d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon        return account;
87068d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon    }
87168d1a6b0bd8840b74c61a94928610312c09b0486Santos Cordon}
872