16c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie/*
2ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * Copyright (C) 2012 The Android Open Source Project
3ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu *
4ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * Licensed under the Apache License, Version 2.0 (the "License");
5ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * you may not use this file except in compliance with the License.
6ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * You may obtain a copy of the License at
7ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu *
8ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu *      http://www.apache.org/licenses/LICENSE-2.0
9ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu *
10ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * Unless required by applicable law or agreed to in writing, software
11ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * distributed under the License is distributed on an "AS IS" BASIS,
12ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * See the License for the specific language governing permissions and
14ede67c26e7b2564ea35db6d9b3027a269c150e13Zhihai Xu * limitations under the License.
156c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie */
166c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
176c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xiepackage com.android.bluetooth.hfp;
186c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
19dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xuimport android.bluetooth.BluetoothAssignedNumbers;
206c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.bluetooth.BluetoothDevice;
216c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.bluetooth.BluetoothHeadset;
226c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.bluetooth.BluetoothProfile;
236c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.content.Intent;
246c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.media.AudioManager;
250420b4fe3c7794139218821fab49f7f149d0075eJack Heimport android.os.Looper;
266c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.os.Message;
2767ccceab9600aaeec6dc34658c45c074de39aa38Jack Heimport android.os.SystemClock;
282287bc2b01df774378123fe7bc7bca14fee549f7Benjamin Franzimport android.os.UserHandle;
29122e077f24750ee0e7fd650cbfa832edeb216d07Jack Heimport android.support.annotation.VisibleForTesting;
306c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.telephony.PhoneNumberUtils;
31448309eada01c130b2fee8977f7fd74875978cbcJack Heimport android.telephony.PhoneStateListener;
326c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport android.util.Log;
33c4fbd756e2645147470c486ae96f2253f5e13a52Jack He
34caa5d6ab09bf61f4379413299ae1ab436c503710Jack Heimport com.android.bluetooth.btservice.AdapterService;
35838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwoodimport com.android.bluetooth.btservice.ProfileService;
366c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport com.android.internal.util.State;
376c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport com.android.internal.util.StateMachine;
38c4fbd756e2645147470c486ae96f2253f5e13a52Jack He
39122e077f24750ee0e7fd650cbfa832edeb216d07Jack Heimport java.io.FileDescriptor;
40122e077f24750ee0e7fd650cbfa832edeb216d07Jack Heimport java.io.PrintWriter;
41122e077f24750ee0e7fd650cbfa832edeb216d07Jack Heimport java.io.StringWriter;
426c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xieimport java.util.ArrayList;
43cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jeeimport java.util.HashMap;
44cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jeeimport java.util.Map;
4567ccceab9600aaeec6dc34658c45c074de39aa38Jack Heimport java.util.Objects;
4667ccceab9600aaeec6dc34658c45c074de39aa38Jack Heimport java.util.Scanner;
476c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
48122e077f24750ee0e7fd650cbfa832edeb216d07Jack He/**
4967ccceab9600aaeec6dc34658c45c074de39aa38Jack He * A Bluetooth Handset StateMachine
50122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                        (Disconnected)
51122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           |      ^
52122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                   CONNECT |      | DISCONNECTED
53122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           V      |
54122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                  (Connecting)   (Disconnecting)
55122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           |      ^
56122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                 CONNECTED |      | DISCONNECT
57122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           V      |
58122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                          (Connected)
59122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           |      ^
60122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *             CONNECT_AUDIO |      | AUDIO_DISCONNECTED
61122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           V      |
62122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *             (AudioConnecting)   (AudioDiconnecting)
63122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           |      ^
64122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *           AUDIO_CONNECTED |      | DISCONNECT_AUDIO
65122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           V      |
66122e077f24750ee0e7fd650cbfa832edeb216d07Jack He *                           (AudioOn)
67122e077f24750ee0e7fd650cbfa832edeb216d07Jack He */
6867ccceab9600aaeec6dc34658c45c074de39aa38Jack He@VisibleForTesting
6967ccceab9600aaeec6dc34658c45c074de39aa38Jack Hepublic class HeadsetStateMachine extends StateMachine {
706c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    private static final String TAG = "HeadsetStateMachine";
712e8302083499fdf5bb391d055e54c3402b0dc556Andre Eisenbach    private static final boolean DBG = false;
726c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
7368e7fc4f2c62ebc76e86109a919592ad25ec11d7Ravi Nagarajan    private static final String HEADSET_NAME = "bt_headset_name";
7468e7fc4f2c62ebc76e86109a919592ad25ec11d7Ravi Nagarajan    private static final String HEADSET_NREC = "bt_headset_nrec";
75bf765c090de4b973f4eede4f4248f5e422454c2dMatthew Xie    private static final String HEADSET_WBS = "bt_wbs";
7667ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private static final String HEADSET_AUDIO_FEATURE_ON = "on";
7767ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private static final String HEADSET_AUDIO_FEATURE_OFF = "off";
7868e7fc4f2c62ebc76e86109a919592ad25ec11d7Ravi Nagarajan
796c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static final int CONNECT = 1;
806c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static final int DISCONNECT = 2;
816c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static final int CONNECT_AUDIO = 3;
826c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static final int DISCONNECT_AUDIO = 4;
836c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static final int VOICE_RECOGNITION_START = 5;
846c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static final int VOICE_RECOGNITION_STOP = 6;
856c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
866c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION
876c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO
886c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static final int INTENT_SCO_VOLUME_CHANGED = 7;
8950b51beb3bd0885ced514d1bcfda3050f60833ffJack He    static final int INTENT_CONNECTION_ACCESS_REPLY = 8;
906c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static final int CALL_STATE_CHANGED = 9;
9167ccceab9600aaeec6dc34658c45c074de39aa38Jack He    static final int DEVICE_STATE_CHANGED = 10;
9267ccceab9600aaeec6dc34658c45c074de39aa38Jack He    static final int SEND_CCLC_RESPONSE = 11;
9367ccceab9600aaeec6dc34658c45c074de39aa38Jack He    static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 12;
9467ccceab9600aaeec6dc34658c45c074de39aa38Jack He    static final int SEND_BSIR = 13;
95a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    static final int DIALING_OUT_RESULT = 14;
96ff5993924ef627e8b723d33ccb5514cb15bb134dJack He    static final int VOICE_RECOGNITION_RESULT = 15;
976b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M
980420b4fe3c7794139218821fab49f7f149d0075eJack He    static final int STACK_EVENT = 101;
9969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private static final int CLCC_RSP_TIMEOUT = 104;
1006c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1016c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    private static final int CONNECT_TIMEOUT = 201;
1026c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
10367ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private static final int CLCC_RSP_TIMEOUT_MS = 5000;
104122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    // NOTE: the value is not "final" - it is modified in the unit tests
10567ccceab9600aaeec6dc34658c45c074de39aa38Jack He    @VisibleForTesting static int sConnectTimeoutMs = 30000;
106122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
107448309eada01c130b2fee8977f7fd74875978cbcJack He    private static final HeadsetAgIndicatorEnableState DEFAULT_AG_INDICATOR_ENABLE_STATE =
108448309eada01c130b2fee8977f7fd74875978cbcJack He            new HeadsetAgIndicatorEnableState(true, true, true, true);
109448309eada01c130b2fee8977f7fd74875978cbcJack He
11067ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private final BluetoothDevice mDevice;
111122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
112122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    // State machine states
113122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private final Disconnected mDisconnected = new Disconnected();
114122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private final Connecting mConnecting = new Connecting();
115122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private final Disconnecting mDisconnecting = new Disconnecting();
116122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private final Connected mConnected = new Connected();
117122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private final AudioOn mAudioOn = new AudioOn();
118122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private final AudioConnecting mAudioConnecting = new AudioConnecting();
119122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private final AudioDisconnecting mAudioDisconnecting = new AudioDisconnecting();
120122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private HeadsetStateBase mPrevState;
121122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
122122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    // Run time dependencies
123caa5d6ab09bf61f4379413299ae1ab436c503710Jack He    private final HeadsetService mHeadsetService;
124caa5d6ab09bf61f4379413299ae1ab436c503710Jack He    private final AdapterService mAdapterService;
12550b51beb3bd0885ced514d1bcfda3050f60833ffJack He    private final HeadsetNativeInterface mNativeInterface;
12650b51beb3bd0885ced514d1bcfda3050f60833ffJack He    private final HeadsetSystemInterface mSystemInterface;
1276c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
128122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    // Runtime states
12950b51beb3bd0885ced514d1bcfda3050f60833ffJack He    private int mSpeakerVolume;
13050b51beb3bd0885ced514d1bcfda3050f60833ffJack He    private int mMicVolume;
131448309eada01c130b2fee8977f7fd74875978cbcJack He    private HeadsetAgIndicatorEnableState mAgIndicatorEnableState;
13267ccceab9600aaeec6dc34658c45c074de39aa38Jack He    // The timestamp when the device entered connecting/connected state
13367ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private long mConnectingTimestampMs = Long.MIN_VALUE;
134122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    // Audio Parameters like NREC
13567ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private final HashMap<String, String> mAudioParams = new HashMap<>();
13650b51beb3bd0885ced514d1bcfda3050f60833ffJack He    // AT Phone book keeps a group of states used by AT+CPBR commands
13750b51beb3bd0885ced514d1bcfda3050f60833ffJack He    private final AtPhonebook mPhonebook;
138a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    // HSP specific
139a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    private boolean mNeedDialingOutReply;
140122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
141122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    // Keys are AT commands, and values are the company IDs.
142122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID;
1436c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1446c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    static {
145122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<>();
1468536d97c0196cdbf1ee288ffe4edc377f3833019Jack He        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put(
1478536d97c0196cdbf1ee288ffe4edc377f3833019Jack He                BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT,
1488536d97c0196cdbf1ee288ffe4edc377f3833019Jack He                BluetoothAssignedNumbers.PLANTRONICS);
1498536d97c0196cdbf1ee288ffe4edc377f3833019Jack He        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put(
1508536d97c0196cdbf1ee288ffe4edc377f3833019Jack He                BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID,
1518536d97c0196cdbf1ee288ffe4edc377f3833019Jack He                BluetoothAssignedNumbers.GOOGLE);
152f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put(
153f27d360b7ec35eaddee66cac173c51c18c238b03Jack He                BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL,
154f27d360b7ec35eaddee66cac173c51c18c238b03Jack He                BluetoothAssignedNumbers.APPLE);
155f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put(
156f27d360b7ec35eaddee66cac173c51c18c238b03Jack He                BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV,
157f27d360b7ec35eaddee66cac173c51c18c238b03Jack He                BluetoothAssignedNumbers.APPLE);
1586c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
1596c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
160caa5d6ab09bf61f4379413299ae1ab436c503710Jack He    private HeadsetStateMachine(BluetoothDevice device, Looper looper,
161caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            HeadsetService headsetService, AdapterService adapterService,
16250b51beb3bd0885ced514d1bcfda3050f60833ffJack He            HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) {
16367ccceab9600aaeec6dc34658c45c074de39aa38Jack He        super(TAG, Objects.requireNonNull(looper, "looper cannot be null"));
164122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        // Enable/Disable StateMachine debug logs
165122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        setDbg(DBG);
16667ccceab9600aaeec6dc34658c45c074de39aa38Jack He        mDevice = Objects.requireNonNull(device, "device cannot be null");
167caa5d6ab09bf61f4379413299ae1ab436c503710Jack He        mHeadsetService = Objects.requireNonNull(headsetService, "headsetService cannot be null");
16867ccceab9600aaeec6dc34658c45c074de39aa38Jack He        mNativeInterface =
16967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                Objects.requireNonNull(nativeInterface, "nativeInterface cannot be null");
17067ccceab9600aaeec6dc34658c45c074de39aa38Jack He        mSystemInterface =
17167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                Objects.requireNonNull(systemInterface, "systemInterface cannot be null");
172caa5d6ab09bf61f4379413299ae1ab436c503710Jack He        mAdapterService = Objects.requireNonNull(adapterService, "AdapterService cannot be null");
17367ccceab9600aaeec6dc34658c45c074de39aa38Jack He        // Create phonebook helper
174caa5d6ab09bf61f4379413299ae1ab436c503710Jack He        mPhonebook = new AtPhonebook(mHeadsetService, mNativeInterface);
175122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        // Initialize state machine
1766c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        addState(mDisconnected);
177122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        addState(mConnecting);
178122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        addState(mDisconnecting);
1796c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        addState(mConnected);
1806c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        addState(mAudioOn);
181122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        addState(mAudioConnecting);
182122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        addState(mAudioDisconnecting);
1836c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        setInitialState(mDisconnected);
1846c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
1856c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
186caa5d6ab09bf61f4379413299ae1ab436c503710Jack He    static HeadsetStateMachine make(BluetoothDevice device, Looper looper,
187caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            HeadsetService headsetService, AdapterService adapterService,
18850b51beb3bd0885ced514d1bcfda3050f60833ffJack He            HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) {
1890420b4fe3c7794139218821fab49f7f149d0075eJack He        HeadsetStateMachine stateMachine =
190caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                new HeadsetStateMachine(device, looper, headsetService, adapterService,
191caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                        nativeInterface, systemInterface);
1920420b4fe3c7794139218821fab49f7f149d0075eJack He        stateMachine.start();
193caa5d6ab09bf61f4379413299ae1ab436c503710Jack He        Log.i(TAG, "Created state machine " + stateMachine + " for " + device);
1940420b4fe3c7794139218821fab49f7f149d0075eJack He        return stateMachine;
1950420b4fe3c7794139218821fab49f7f149d0075eJack He    }
1960420b4fe3c7794139218821fab49f7f149d0075eJack He
1970420b4fe3c7794139218821fab49f7f149d0075eJack He    static void destroy(HeadsetStateMachine stateMachine) {
1980420b4fe3c7794139218821fab49f7f149d0075eJack He        Log.i(TAG, "destroy");
1990420b4fe3c7794139218821fab49f7f149d0075eJack He        if (stateMachine == null) {
2000420b4fe3c7794139218821fab49f7f149d0075eJack He            Log.w(TAG, "destroy(), stateMachine is null");
2010420b4fe3c7794139218821fab49f7f149d0075eJack He            return;
2020420b4fe3c7794139218821fab49f7f149d0075eJack He        }
203122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        stateMachine.quitNow();
2040420b4fe3c7794139218821fab49f7f149d0075eJack He        stateMachine.cleanup();
2056893668d1ccf0cc6361ef31ace52fedc2e37e35aWink Saville    }
2066893668d1ccf0cc6361ef31ace52fedc2e37e35aWink Saville
2076654f5c903de510a70f9e72cd5ad7837b615d93ffredc    public void cleanup() {
20874ae04c73312403e89db0f8e9bd9601d403b4783fredc        if (mPhonebook != null) {
20974ae04c73312403e89db0f8e9bd9601d403b4783fredc            mPhonebook.cleanup();
21074ae04c73312403e89db0f8e9bd9601d403b4783fredc        }
211122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        mAudioParams.clear();
2126654f5c903de510a70f9e72cd5ad7837b615d93ffredc    }
2136654f5c903de510a70f9e72cd5ad7837b615d93ffredc
214838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood    public void dump(StringBuilder sb) {
21567ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  mCurrentDevice: " + mDevice);
21667ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  mCurrentState: " + getCurrentState());
21767ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  mPrevState: " + mPrevState);
21867ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  mConnectionState: " + getConnectionState());
21967ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  mAudioState: " + getAudioState());
220a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        ProfileService.println(sb, "  mNeedDialingOutReply: " + mNeedDialingOutReply);
22167ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  mSpeakerVolume: " + mSpeakerVolume);
22267ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  mMicVolume: " + mMicVolume);
22367ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb,
22467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                "  mConnectingTimestampMs(uptimeMillis): " + mConnectingTimestampMs);
22567ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  StateMachine: " + this);
226122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        // Dump the state machine logs
227122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        StringWriter stringWriter = new StringWriter();
228122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        PrintWriter printWriter = new PrintWriter(stringWriter);
229122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        super.dump(new FileDescriptor(), printWriter, new String[]{});
230122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        printWriter.flush();
231122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        stringWriter.flush();
23267ccceab9600aaeec6dc34658c45c074de39aa38Jack He        ProfileService.println(sb, "  StateMachineLog:");
23367ccceab9600aaeec6dc34658c45c074de39aa38Jack He        Scanner scanner = new Scanner(stringWriter.toString());
23467ccceab9600aaeec6dc34658c45c074de39aa38Jack He        while (scanner.hasNextLine()) {
23567ccceab9600aaeec6dc34658c45c074de39aa38Jack He            String line = scanner.nextLine();
23667ccceab9600aaeec6dc34658c45c074de39aa38Jack He            ProfileService.println(sb, "    " + line);
23767ccceab9600aaeec6dc34658c45c074de39aa38Jack He        }
23867ccceab9600aaeec6dc34658c45c074de39aa38Jack He        scanner.close();
239838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood    }
240838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood
241122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    /**
242122e077f24750ee0e7fd650cbfa832edeb216d07Jack He     * Base class for states used in this state machine to share common infrastructures
243122e077f24750ee0e7fd650cbfa832edeb216d07Jack He     */
244122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private abstract class HeadsetStateBase extends State {
245122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
246122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public void enter() {
247122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            // Crash if mPrevState is null and state is not Disconnected
248122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            if (!(this instanceof Disconnected) && mPrevState == null) {
249122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                throw new IllegalStateException("mPrevState is null on enter()");
250122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
251122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            enforceValidConnectionStateTransition();
252122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
253122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
254122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
255122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public void exit() {
256122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            mPrevState = this;
257122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
258122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
259122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
260122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public String toString() {
261122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return getName();
262122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
263122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
264122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        /**
265122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * Broadcast audio and connection state changes to the system. This should be called at the
266122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * end of enter() method after all the setup is done
267122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         */
268122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void broadcastStateTransitions() {
26967ccceab9600aaeec6dc34658c45c074de39aa38Jack He            if (mPrevState == null) {
270122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return;
271122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
272122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            // TODO: Add STATE_AUDIO_DISCONNECTING constant to get rid of the 2nd part of this logic
273122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            if (getAudioStateInt() != mPrevState.getAudioStateInt() || (
274122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    mPrevState instanceof AudioDisconnecting && this instanceof AudioOn)) {
27567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                stateLogD("audio state changed: " + mDevice + ": " + mPrevState + " -> " + this);
27667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                broadcastAudioState(mDevice, mPrevState.getAudioStateInt(), getAudioStateInt());
277122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
278122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            if (getConnectionStateInt() != mPrevState.getConnectionStateInt()) {
27967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                stateLogD(
28067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        "connection state changed: " + mDevice + ": " + mPrevState + " -> " + this);
28167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                broadcastConnectionState(mDevice, mPrevState.getConnectionStateInt(),
282122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        getConnectionStateInt());
283122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
284122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
285122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
286122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        // Should not be called from enter() method
287122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) {
288122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            stateLogD("broadcastConnectionState " + device + ": " + fromState + "->" + toState);
289caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            mHeadsetService.onConnectionStateChangedFromStateMachine(device, fromState, toState);
290122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
291122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
292122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
293122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
294122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
295caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL,
296caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                    HeadsetService.BLUETOOTH_PERM);
297122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
298122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
299122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        // Should not be called from enter() method
300122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void broadcastAudioState(BluetoothDevice device, int fromState, int toState) {
301122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            stateLogD("broadcastAudioState: " + device + ": " + fromState + "->" + toState);
302caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            mHeadsetService.onAudioStateChangedFromStateMachine(device, fromState, toState);
303122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
304122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
305122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
306122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
307caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL,
308caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                    HeadsetService.BLUETOOTH_PERM);
309122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
310122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
311122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        /**
312122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * Verify if the current state transition is legal. This is supposed to be called from
313122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * enter() method and crash if the state transition is out of the specification
314122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         *
315122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * Note:
316122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * This method uses state objects to verify transition because these objects should be final
317122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * and any other instances are invalid
318122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         */
319122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void enforceValidConnectionStateTransition() {
320122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            boolean result = false;
321122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            if (this == mDisconnected) {
322122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                result = mPrevState == null || mPrevState == mConnecting
323122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        || mPrevState == mDisconnecting
324122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // TODO: edges to be removed after native stack refactoring
325122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // all transitions to disconnected state should go through a pending state
326122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // also, states should not go directly from an active audio state to
327122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // disconnected state
328122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        || mPrevState == mConnected || mPrevState == mAudioOn
329122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        || mPrevState == mAudioConnecting || mPrevState == mAudioDisconnecting;
330122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            } else if (this == mConnecting) {
331122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                result = mPrevState == mDisconnected;
332122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            } else if (this == mDisconnecting) {
333122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                result = mPrevState == mConnected
334122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // TODO: edges to be removed after native stack refactoring
335122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // all transitions to disconnecting state should go through connected state
336122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        || mPrevState == mAudioConnecting || mPrevState == mAudioOn
337122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        || mPrevState == mAudioDisconnecting;
338122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            } else if (this == mConnected) {
339122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                result = mPrevState == mConnecting || mPrevState == mAudioDisconnecting
340122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        || mPrevState == mDisconnecting || mPrevState == mAudioConnecting
341122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // TODO: edges to be removed after native stack refactoring
342122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // all transitions to connected state should go through a pending state
343122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        || mPrevState == mAudioOn || mPrevState == mDisconnected;
344122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            } else if (this == mAudioConnecting) {
345122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                result = mPrevState == mConnected;
346122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            } else if (this == mAudioDisconnecting) {
347122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                result = mPrevState == mAudioOn;
348122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            } else if (this == mAudioOn) {
349122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                result = mPrevState == mAudioConnecting || mPrevState == mAudioDisconnecting
350122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // TODO: edges to be removed after native stack refactoring
351122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // all transitions to audio connected state should go through a pending
352122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // state
353122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        || mPrevState == mConnected;
354122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
355122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            if (!result) {
356122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                throw new IllegalStateException(
357122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        "Invalid state transition from " + mPrevState + " to " + this
35867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + " for device " + mDevice);
359122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
360122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
361122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
362122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void stateLogD(String msg) {
36367ccceab9600aaeec6dc34658c45c074de39aa38Jack He            log(getName() + ": currentDevice=" + mDevice + ", msg=" + msg);
364122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
365122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
366122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void stateLogW(String msg) {
36767ccceab9600aaeec6dc34658c45c074de39aa38Jack He            logw(getName() + ": currentDevice=" + mDevice + ", msg=" + msg);
368122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
369122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
370122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void stateLogE(String msg) {
37167ccceab9600aaeec6dc34658c45c074de39aa38Jack He            loge(getName() + ": currentDevice=" + mDevice + ", msg=" + msg);
372122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
373122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
374122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void stateLogV(String msg) {
37567ccceab9600aaeec6dc34658c45c074de39aa38Jack He            logv(getName() + ": currentDevice=" + mDevice + ", msg=" + msg);
376122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
377122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
378122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void stateLogI(String msg) {
37967ccceab9600aaeec6dc34658c45c074de39aa38Jack He            logi(getName() + ": currentDevice=" + mDevice + ", msg=" + msg);
380122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
381122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
382122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        void stateLogWtfStack(String msg) {
383122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            Log.wtfStack(TAG, getName() + ": " + msg);
384122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
385122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
386122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        /**
387122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * Process connection event
388122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         *
389122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * @param message the current message for the event
390122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * @param state connection state to transition to
391122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         */
39267ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public abstract void processConnectionEvent(Message message, int state);
393122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
394122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        /**
395122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * Get a state value from {@link BluetoothProfile} that represents the connection state of
396122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * this headset state
397122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         *
398122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * @return a value in {@link BluetoothProfile#STATE_DISCONNECTED},
399122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or
400122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * {@link BluetoothProfile#STATE_DISCONNECTING}
401122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         */
402122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        abstract int getConnectionStateInt();
403122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
404122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        /**
405122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * Get an audio state value from {@link BluetoothHeadset}
406122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * @return a value in {@link BluetoothHeadset#STATE_AUDIO_DISCONNECTED},
407122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * {@link BluetoothHeadset#STATE_AUDIO_CONNECTING}, or
408122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * {@link BluetoothHeadset#STATE_AUDIO_CONNECTED}
409122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         */
410122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        abstract int getAudioStateInt();
411122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
412122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    }
413122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
414122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    class Disconnected extends HeadsetStateBase {
415122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
416122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getConnectionStateInt() {
417122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothProfile.STATE_DISCONNECTED;
418122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
419122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
420122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
421122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getAudioStateInt() {
422122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
423122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
424122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
4256c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
4266c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public void enter() {
427122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.enter();
42867ccceab9600aaeec6dc34658c45c074de39aa38Jack He            mConnectingTimestampMs = Long.MIN_VALUE;
429405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            mPhonebook.resetAtState();
430448309eada01c130b2fee8977f7fd74875978cbcJack He            updateAgIndicatorEnableState(null);
431a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            mNeedDialingOutReply = false;
432122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            mAudioParams.clear();
433122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            broadcastStateTransitions();
434caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            // Remove the state machine for unbonded devices
435caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            if (mPrevState != null
436caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                    && mAdapterService.getBondState(mDevice) == BluetoothDevice.BOND_NONE) {
437caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                getHandler().post(() -> mHeadsetService.removeStateMachine(mDevice));
438caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            }
4396c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
4406c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
4416c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
4426c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean processMessage(Message message) {
4433659dee2b21c7f269a2bef051483093fe07e5682Jack He            switch (message.what) {
4446c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case CONNECT:
4456c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    BluetoothDevice device = (BluetoothDevice) message.obj;
446122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("Connecting to " + device);
44767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
44867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE(
44967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                "CONNECT failed, device=" + device + ", currentDevice=" + mDevice);
45067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
45167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
4520420b4fe3c7794139218821fab49f7f149d0075eJack He                    if (!mNativeInterface.connectHfp(device)) {
45367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("CONNECT failed for connectHfp(" + device + ")");
454122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // No state transition is involved, fire broadcast immediately
4556c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
456122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                                BluetoothProfile.STATE_DISCONNECTED);
4576c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                        break;
4586c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    }
459122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mConnecting);
4606c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
4616c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case DISCONNECT:
4626c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    // ignore
4636c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
4646c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case CALL_STATE_CHANGED:
46567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogD("Ignoring CALL_STATE_CHANGED event");
4666c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
46739af3989003fd06fb3da55904ee074f706e1d448Jack He                case DEVICE_STATE_CHANGED:
468122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("Ignoring DEVICE_STATE_CHANGED event");
46939af3989003fd06fb3da55904ee074f706e1d448Jack He                    break;
4706c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case STACK_EVENT:
4710420b4fe3c7794139218821fab49f7f149d0075eJack He                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
472122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("STACK_EVENT: " + event);
47367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(event.device)) {
47467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("Event device does not match currentDevice[" + mDevice
47567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + "], event: " + event);
47667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
47767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
4786c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    switch (event.type) {
4790420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
48067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            processConnectionEvent(message, event.valueInt);
4816c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
4826c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                        default:
483122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogE("Unexpected stack event: " + event);
4846c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
4856c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    }
4866c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
4876c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                default:
488122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
4896c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    return NOT_HANDLED;
4906c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
491c6231925ec13a432770a879c49b3c4f008ee96e1Jack He            return HANDLED;
4926c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
4936c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
494122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
49567ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public void processConnectionEvent(Message message, int state) {
49667ccceab9600aaeec6dc34658c45c074de39aa38Jack He            stateLogD("processConnectionEvent, state=" + state);
4976c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            switch (state) {
4983659dee2b21c7f269a2bef051483093fe07e5682Jack He                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
49967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("ignore DISCONNECTED event");
5003659dee2b21c7f269a2bef051483093fe07e5682Jack He                    break;
501122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                // Both events result in Connecting state as SLC establishment is still required
50239af3989003fd06fb3da55904ee074f706e1d448Jack He                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
5033659dee2b21c7f269a2bef051483093fe07e5682Jack He                case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
504caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                    if (mHeadsetService.okToAcceptConnection(mDevice)) {
50567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogI("accept incoming connection");
506122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        transitionTo(mConnecting);
5073659dee2b21c7f269a2bef051483093fe07e5682Jack He                    } else {
508caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                        stateLogI("rejected incoming HF, priority=" + mHeadsetService.getPriority(
509caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                                mDevice) + " bondState=" + mAdapterService.getBondState(mDevice));
510122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // Reject the connection and stay in Disconnected state itself
51167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        if (!mNativeInterface.disconnectHfp(mDevice)) {
51267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            stateLogE("failed to disconnect");
513122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        }
514122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // Indicate rejection to other components.
51567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        broadcastConnectionState(mDevice, BluetoothProfile.STATE_DISCONNECTED,
516bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                                BluetoothProfile.STATE_DISCONNECTED);
51775e9fd59f4d4011ba7155732a41b06f0df40badaSwaminatha Balaji                    }
5183659dee2b21c7f269a2bef051483093fe07e5682Jack He                    break;
5193659dee2b21c7f269a2bef051483093fe07e5682Jack He                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
52067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("Ignore DISCONNECTING event");
5213659dee2b21c7f269a2bef051483093fe07e5682Jack He                    break;
5223659dee2b21c7f269a2bef051483093fe07e5682Jack He                default:
523122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogE("Incorrect state: " + state);
5243659dee2b21c7f269a2bef051483093fe07e5682Jack He                    break;
5256c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
5266c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
5276c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
5286c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
52939af3989003fd06fb3da55904ee074f706e1d448Jack He    // Per HFP 1.7.1 spec page 23/144, Pending state needs to handle
53039af3989003fd06fb3da55904ee074f706e1d448Jack He    //      AT+BRSF, AT+CIND, AT+CMER, AT+BIND, +CHLD
53139af3989003fd06fb3da55904ee074f706e1d448Jack He    // commands during SLC establishment
532122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    class Connecting extends HeadsetStateBase {
533122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
534122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getConnectionStateInt() {
535122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothProfile.STATE_CONNECTING;
536122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
537122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
538122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
539122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getAudioStateInt() {
540122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
541122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
542122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
5436c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
5446c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public void enter() {
545122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.enter();
54667ccceab9600aaeec6dc34658c45c074de39aa38Jack He            mConnectingTimestampMs = SystemClock.uptimeMillis();
54767ccceab9600aaeec6dc34658c45c074de39aa38Jack He            sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs);
548122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            broadcastStateTransitions();
5496c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
5506c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
5516c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
5526c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean processMessage(Message message) {
5533659dee2b21c7f269a2bef051483093fe07e5682Jack He            switch (message.what) {
5546c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case CONNECT:
555345d21b0575a4b866bfc9ccfde9c654e7b859ac6Matthew Xie                case CONNECT_AUDIO:
556122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT:
5576c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    deferMessage(message);
5586c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
55967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                case CONNECT_TIMEOUT: {
560122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // We timed out trying to connect, transition to Disconnected state
56167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
56267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
56367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("Unknown device timeout " + device);
56467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
56567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
56667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("CONNECT_TIMEOUT");
567122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mDisconnected);
5686c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
56967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                }
5706c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case CALL_STATE_CHANGED:
57167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogD("ignoring CALL_STATE_CHANGED event");
5726c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
57339af3989003fd06fb3da55904ee074f706e1d448Jack He                case DEVICE_STATE_CHANGED:
574122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("ignoring DEVICE_STATE_CHANGED event");
57539af3989003fd06fb3da55904ee074f706e1d448Jack He                    break;
5766c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case STACK_EVENT:
5770420b4fe3c7794139218821fab49f7f149d0075eJack He                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
578122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("STACK_EVENT: " + event);
57967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(event.device)) {
58067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("Event device does not match currentDevice[" + mDevice
58167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + "], event: " + event);
58267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
58367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
5846c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    switch (event.type) {
5850420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
58667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            processConnectionEvent(message, event.valueInt);
5876c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
5880420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AT_CHLD:
58939af3989003fd06fb3da55904ee074f706e1d448Jack He                            processAtChld(event.valueInt, event.device);
59039af3989003fd06fb3da55904ee074f706e1d448Jack He                            break;
5910420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AT_CIND:
59239af3989003fd06fb3da55904ee074f706e1d448Jack He                            processAtCind(event.device);
59339af3989003fd06fb3da55904ee074f706e1d448Jack He                            break;
5940420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_WBS:
595122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            processWBSEvent(event.valueInt);
59639af3989003fd06fb3da55904ee074f706e1d448Jack He                            break;
5970420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_BIND:
5982ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                            processAtBind(event.valueString, event.device);
5992ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                            break;
600bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                        // Unexpected AT commands, we only handle them for comparability reasons
6010420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED:
602122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected VR event, device=" + event.device + ", state="
603122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                                    + event.valueInt);
604ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                            processVrEvent(event.valueInt);
605bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6060420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL:
607122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected dial event, device=" + event.device);
608a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                            processDialCall(event.valueString);
609bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6100420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
611122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected subscriber number event for" + event.device
612122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                                    + ", state=" + event.valueInt);
613bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            processSubscriberNumberRequest(event.device);
614bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6150420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AT_COPS:
616122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected COPS event for " + event.device);
617bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            processAtCops(event.device);
618bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6190420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AT_CLCC:
620122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            Log.w(TAG, "Connecting: Unexpected CLCC event for" + event.device);
621bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            processAtClcc(event.device);
622bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6230420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT:
624122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected unknown AT event for" + event.device + ", cmd="
625122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                                    + event.valueString);
626bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            processUnknownAt(event.valueString, event.device);
627bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6280420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED:
629122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected key-press event for " + event.device);
630bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            processKeyPressed(event.device);
631bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6320420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_BIEV:
633122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected BIEV event for " + event.device + ", indId="
634122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                                    + event.valueInt + ", indVal=" + event.valueInt2);
635bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            processAtBiev(event.valueInt, event.valueInt2, event.device);
636bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6370420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED:
638122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected volume event for " + event.device);
63967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            processVolumeEvent(event.valueInt, event.valueInt2);
640bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6410420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL:
642122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected answer event for " + event.device);
64350b51beb3bd0885ced514d1bcfda3050f60833ffJack He                            mSystemInterface.answerCall(event.device);
644bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6450420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL:
646122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogW("Unexpected hangup event for " + event.device);
647a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                            mSystemInterface.hangupCall(event.device);
648bc67ac7b9ef7a6b33fc550257b75726d86641923Jack He                            break;
6496c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                        default:
650122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogE("Unexpected event: " + event);
6516c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
6526c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    }
6536c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
6546c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                default:
655122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
6566c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    return NOT_HANDLED;
6576c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
658c6231925ec13a432770a879c49b3c4f008ee96e1Jack He            return HANDLED;
6596c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
6606c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
661122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
66267ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public void processConnectionEvent(Message message, int state) {
66367ccceab9600aaeec6dc34658c45c074de39aa38Jack He            stateLogD("processConnectionEvent, state=" + state);
6646c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            switch (state) {
6656c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
66667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("Disconnected");
667122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mDisconnected);
6686c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
66969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
67067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogD("RFCOMM connected");
67139af3989003fd06fb3da55904ee074f706e1d448Jack He                    break;
67239af3989003fd06fb3da55904ee074f706e1d448Jack He                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
67367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogD("SLC connected");
674122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mConnected);
67569d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
67669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
677122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // Ignored
67869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
67969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
68067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("Disconnecting");
68169d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
68269d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                default:
683122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogE("Incorrect state " + state);
68469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
6856c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
6866c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
6876c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
688122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
689122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public void exit() {
690122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            removeMessages(CONNECT_TIMEOUT);
691122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.exit();
69269d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        }
6936c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
6946c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
695122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    class Disconnecting extends HeadsetStateBase {
696122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
697122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getConnectionStateInt() {
698122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothProfile.STATE_DISCONNECTING;
699122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
700122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
701122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
702122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getAudioStateInt() {
703122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
704122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
705122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
7066c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
7076c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public void enter() {
708122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.enter();
70967ccceab9600aaeec6dc34658c45c074de39aa38Jack He            sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs);
710122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            broadcastStateTransitions();
7116c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
7126c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
7136c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
7146c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean processMessage(Message message) {
7153659dee2b21c7f269a2bef051483093fe07e5682Jack He            switch (message.what) {
716122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT:
717122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_AUDIO:
718122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT:
719122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    deferMessage(message);
720122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
72167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                case CONNECT_TIMEOUT: {
72267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
72367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
72467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("Unknown device timeout " + device);
72567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
72667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
727122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogE("timeout");
728122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mDisconnected);
729122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
73067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                }
731122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case STACK_EVENT:
732122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
733122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("STACK_EVENT: " + event);
73467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(event.device)) {
73567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("Event device does not match currentDevice[" + mDevice
73667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + "], event: " + event);
73767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
73867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
739122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    switch (event.type) {
740122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
74167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            processConnectionEvent(message, event.valueInt);
7423659dee2b21c7f269a2bef051483093fe07e5682Jack He                            break;
743122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        default:
744122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogE("Unexpected event: " + event);
7453659dee2b21c7f269a2bef051483093fe07e5682Jack He                            break;
74669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    }
747122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
748122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                default:
749122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
750122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    return NOT_HANDLED;
751122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
752122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return HANDLED;
753122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
754122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
755122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        // in Disconnecting state
756122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
75767ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public void processConnectionEvent(Message message, int state) {
758122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            switch (state) {
759122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
76067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogD("processConnectionEvent: Disconnected");
761122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mDisconnected);
762122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
763122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
76467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogD("processConnectionEvent: Connected");
765122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mConnected);
766122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
767122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                default:
76867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogE("processConnectionEvent: Bad state: " + state);
769122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
770122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
771122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
772122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
773122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
774122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public void exit() {
775122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            removeMessages(CONNECT_TIMEOUT);
776122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.exit();
777122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
778122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    }
779122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
780122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    /**
781122e077f24750ee0e7fd650cbfa832edeb216d07Jack He     * Base class for Connected, AudioConnecting, AudioOn, AudioDisconnecting states
782122e077f24750ee0e7fd650cbfa832edeb216d07Jack He     */
783122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private abstract class ConnectedBase extends HeadsetStateBase {
784122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
785122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getConnectionStateInt() {
786122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothProfile.STATE_CONNECTED;
787122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
788122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
789122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        /**
790122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * Handle common messages in connected states. However, state specific messages must be
791122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * handled individually.
792122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         *
793122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * @param message Incoming message to handle
794122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * @return True if handled successfully, False otherwise
795122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         */
796122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
797122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public boolean processMessage(Message message) {
798122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            switch (message.what) {
799122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT:
800122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT:
801122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_AUDIO:
802122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT_AUDIO:
803122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_TIMEOUT:
80467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    throw new IllegalStateException(
80567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            "Illegal message in generic handler: " + message);
80667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                case VOICE_RECOGNITION_START: {
80767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
80867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
80967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogW("VOICE_RECOGNITION_START failed " + device
81067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + " is not currentDevice");
81167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
81267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
813ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    if (!mNativeInterface.startVoiceRecognition(mDevice)) {
814ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                        stateLogW("Failed to start voice recognition");
815ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                        break;
816ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    }
817b18d6a7da48a6847df88abadd42fbb51316cb76dRavi Nagarajan                    break;
81867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                }
81967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                case VOICE_RECOGNITION_STOP: {
82067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
82167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
82267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogW("VOICE_RECOGNITION_STOP failed " + device
82367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + " is not currentDevice");
82467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
82567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
826ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    if (!mNativeInterface.stopVoiceRecognition(mDevice)) {
827ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                        stateLogW("Failed to stop voice recognition");
828ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                        break;
829ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    }
8306c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
83167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                }
832a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                case CALL_STATE_CHANGED: {
833a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                    HeadsetCallState callState = (HeadsetCallState) message.obj;
834a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                    if (!mNativeInterface.phoneStateChange(mDevice, callState)) {
835a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        stateLogW("processCallState: failed to update call state " + callState);
836a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        break;
837a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                    }
8386c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
839a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                }
8406c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case DEVICE_STATE_CHANGED:
84167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    mNativeInterface.notifyDeviceStatus(mDevice, (HeadsetDeviceState) message.obj);
8426c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
8436c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case SEND_CCLC_RESPONSE:
8446c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    processSendClccResponse((HeadsetClccResponse) message.obj);
8456c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
8463659dee2b21c7f269a2bef051483093fe07e5682Jack He                case CLCC_RSP_TIMEOUT: {
84769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    BluetoothDevice device = (BluetoothDevice) message.obj;
84867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
84967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogW("CLCC_RSP_TIMEOUT failed " + device + " is not currentDevice");
85067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
85167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
8520420b4fe3c7794139218821fab49f7f149d0075eJack He                    mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0);
853c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
854c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                break;
855cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
856cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee                    processSendVendorSpecificResultCode(
857cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee                            (HeadsetVendorSpecificResultCode) message.obj);
858cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee                    break;
85967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                case SEND_BSIR:
86067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    mNativeInterface.sendBsir(mDevice, message.arg1 == 1);
86167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    break;
862ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                case VOICE_RECOGNITION_RESULT: {
863ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
864ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    if (!mDevice.equals(device)) {
865ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                        stateLogW("VOICE_RECOGNITION_RESULT failed " + device
866ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                                + " is not currentDevice");
867ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                        break;
868ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    }
869ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    mNativeInterface.atResponseCode(mDevice,
870ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                            message.arg1 == 1 ? HeadsetHalConstants.AT_RESPONSE_OK
871ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                                    : HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
872ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                    break;
873ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                }
874a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                case DIALING_OUT_RESULT: {
87569d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    BluetoothDevice device = (BluetoothDevice) message.obj;
87667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
877ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                        stateLogW("DIALING_OUT_RESULT failed " + device + " is not currentDevice");
87867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
87967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
880a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                    if (mNeedDialingOutReply) {
881a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        mNeedDialingOutReply = false;
882a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        mNativeInterface.atResponseCode(mDevice,
883a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                                message.arg1 == 1 ? HeadsetHalConstants.AT_RESPONSE_OK
884a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                                        : HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
8856c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    }
886c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
887c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                break;
88850b51beb3bd0885ced514d1bcfda3050f60833ffJack He                case INTENT_CONNECTION_ACCESS_REPLY:
88950b51beb3bd0885ced514d1bcfda3050f60833ffJack He                    handleAccessPermissionResult((Intent) message.obj);
89050b51beb3bd0885ced514d1bcfda3050f60833ffJack He                    break;
8916c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case STACK_EVENT:
8920420b4fe3c7794139218821fab49f7f149d0075eJack He                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
893122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("STACK_EVENT: " + event);
89467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(event.device)) {
89567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("Event device does not match currentDevice[" + mDevice
89667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + "], event: " + event);
89767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
89867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
8996c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    switch (event.type) {
9000420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
90167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            processConnectionEvent(message, event.valueInt);
9026c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9030420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
90467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            processAudioEvent(event.valueInt);
9056c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9060420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED:
907ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                            processVrEvent(event.valueInt);
908b18d6a7da48a6847df88abadd42fbb51316cb76dRavi Nagarajan                            break;
9090420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL:
91050b51beb3bd0885ced514d1bcfda3050f60833ffJack He                            mSystemInterface.answerCall(event.device);
9116c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9120420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL:
913a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                            mSystemInterface.hangupCall(event.device);
9146c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9150420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED:
91667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            processVolumeEvent(event.valueInt, event.valueInt2);
9176c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9180420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL:
919a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                            processDialCall(event.valueString);
9206c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9210420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_SEND_DTMF:
92250b51beb3bd0885ced514d1bcfda3050f60833ffJack He                            mSystemInterface.sendDtmf(event.valueInt, event.device);
9236c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9240420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_NOICE_REDUCTION:
92567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            processNoiseReductionEvent(event.valueInt == 1);
92668e7fc4f2c62ebc76e86109a919592ad25ec11d7Ravi Nagarajan                            break;
9270420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_WBS:
928122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            processWBSEvent(event.valueInt);
929b6132733b6e386cc8c93b5598c72ed8efe04bf3eMatthew Xie                            break;
9300420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AT_CHLD:
93169d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                            processAtChld(event.valueInt, event.device);
9326c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9330420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
93469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                            processSubscriberNumberRequest(event.device);
9356c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9360420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AT_CIND:
93769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                            processAtCind(event.device);
9386c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9390420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AT_COPS:
94069d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                            processAtCops(event.device);
9416c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9420420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_AT_CLCC:
94369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                            processAtClcc(event.device);
9446c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9450420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT:
94669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                            processUnknownAt(event.valueString, event.device);
9476c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9480420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED:
94969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                            processKeyPressed(event.device);
9506c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9510420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_BIND:
9522ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                            processAtBind(event.valueString, event.device);
9532ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                            break;
9540420b4fe3c7794139218821fab49f7f149d0075eJack He                        case HeadsetStackEvent.EVENT_TYPE_BIEV:
9552ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                            processAtBiev(event.valueInt, event.valueInt2, event.device);
9562ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                            break;
957448309eada01c130b2fee8977f7fd74875978cbcJack He                        case HeadsetStackEvent.EVENT_TYPE_BIA:
958448309eada01c130b2fee8977f7fd74875978cbcJack He                            updateAgIndicatorEnableState(
959448309eada01c130b2fee8977f7fd74875978cbcJack He                                    (HeadsetAgIndicatorEnableState) event.valueObject);
960448309eada01c130b2fee8977f7fd74875978cbcJack He                            break;
9616c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                        default:
962122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                            stateLogE("Unknown stack event: " + event);
9636c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
9646c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    }
9656c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
9666c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                default:
967122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
9686c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    return NOT_HANDLED;
9696c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
970c6231925ec13a432770a879c49b3c4f008ee96e1Jack He            return HANDLED;
9716c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
9726c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
973122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
97467ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public void processConnectionEvent(Message message, int state) {
97567ccceab9600aaeec6dc34658c45c074de39aa38Jack He            stateLogD("processConnectionEvent, state=" + state);
9766c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            switch (state) {
977122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
97867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogE("processConnectionEvent: RFCOMM connected again, shouldn't happen");
9796c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
980bcbeaf69468424800a939b3e8678eaef21efa3d6Matthew Xie                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
98167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogE("processConnectionEvent: SLC connected again, shouldn't happen");
982122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
983122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
98467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogI("processConnectionEvent: Disconnecting");
985122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mDisconnecting);
98669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
987122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
98867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogI("processConnectionEvent: Disconnected");
989122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mDisconnected);
9906c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
9916c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                default:
99267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogE("processConnectionEvent: bad state: " + state);
9936c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
9946c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
9956c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
996bcbeaf69468424800a939b3e8678eaef21efa3d6Matthew Xie
997122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        /**
998122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * Each state should handle audio events differently
999122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         *
1000122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         * @param state audio state
1001122e077f24750ee0e7fd650cbfa832edeb216d07Jack He         */
100267ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public abstract void processAudioEvent(int state);
10036c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
10046c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1005122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    class Connected extends ConnectedBase {
1006122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1007122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getAudioStateInt() {
1008122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1009122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1010122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
10116c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
10126c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public void enter() {
1013122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.enter();
101467ccceab9600aaeec6dc34658c45c074de39aa38Jack He            if (mConnectingTimestampMs == Long.MIN_VALUE) {
101567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                mConnectingTimestampMs = SystemClock.uptimeMillis();
101667ccceab9600aaeec6dc34658c45c074de39aa38Jack He            }
1017122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            if (mPrevState == mConnecting) {
10186743e015e076aa1a24f92d3be187f9af6d19b303Chienyuan                // Reset AG indicator subscriptions, HF can set this later using AT+BIA command
10196743e015e076aa1a24f92d3be187f9af6d19b303Chienyuan                updateAgIndicatorEnableState(DEFAULT_AG_INDICATOR_ENABLE_STATE);
102067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                // Reset NREC on connect event. Headset will override later
102167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                processNoiseReductionEvent(true);
102267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                // Query phone state for initial setup
102367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                mSystemInterface.queryPhoneState();
1024122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                // Remove pending connection attempts that were deferred during the pending
1025122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                // state. This is to prevent auto connect attempts from disconnecting
1026122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                // devices that previously successfully connected.
1027122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                removeDeferredMessages(CONNECT);
1028122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
1029122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            broadcastStateTransitions();
10306c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
10316c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
10326c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        @Override
10336c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        public boolean processMessage(Message message) {
10343659dee2b21c7f269a2bef051483093fe07e5682Jack He            switch (message.what) {
10353659dee2b21c7f269a2bef051483093fe07e5682Jack He                case CONNECT: {
1036345d21b0575a4b866bfc9ccfde9c654e7b859ac6Matthew Xie                    BluetoothDevice device = (BluetoothDevice) message.obj;
103767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("CONNECT, ignored, device=" + device + ", currentDevice" + mDevice);
103867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    break;
1039c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
10403659dee2b21c7f269a2bef051483093fe07e5682Jack He                case DISCONNECT: {
10413659dee2b21c7f269a2bef051483093fe07e5682Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
1042122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("DISCONNECT from device=" + device);
104367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
1044122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        stateLogW("DISCONNECT, device " + device + " not connected");
104569d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                        break;
104669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    }
1047122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    if (!mNativeInterface.disconnectHfp(device)) {
1048122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // broadcast immediately as no state transition is involved
1049122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        stateLogE("DISCONNECT from " + device + " failed");
1050122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
10513659dee2b21c7f269a2bef051483093fe07e5682Jack He                                BluetoothProfile.STATE_CONNECTED);
1052122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        break;
105369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    }
1054122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mDisconnecting);
1055c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
1056c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                break;
1057122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_AUDIO:
105867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogD("CONNECT_AUDIO, device=" + mDevice);
105927fca676c2809f992575420cf9fe360ae909699bJack He                    mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true");
106067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mNativeInterface.connectAudio(mDevice)) {
106127fca676c2809f992575420cf9fe360ae909699bJack He                        mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false");
106267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("Failed to connect SCO audio for " + mDevice);
1063122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // No state change involved, fire broadcast immediately
106467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        broadcastAudioState(mDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1065122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                                BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1066122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        break;
106769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    }
1068122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mAudioConnecting);
106969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
1070122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT_AUDIO:
107167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogD("ignore DISCONNECT_AUDIO, device=" + mDevice);
1072122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // ignore
107369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
107469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                default:
1075122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    return super.processMessage(message);
107669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            }
1077c6231925ec13a432770a879c49b3c4f008ee96e1Jack He            return HANDLED;
107869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        }
107969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava
1080122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
108167ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public void processAudioEvent(int state) {
108267ccceab9600aaeec6dc34658c45c074de39aa38Jack He            stateLogD("processAudioEvent, state=" + state);
108369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            switch (state) {
1084122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1085a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                    if (!mHeadsetService.isScoAcceptable(mDevice)) {
108667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogW("processAudioEvent: reject incoming audio connection");
108767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        if (!mNativeInterface.disconnectAudio(mDevice)) {
108867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            stateLogE("processAudioEvent: failed to disconnect audio");
108969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                        }
1090a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        // Indicate rejection to other components.
1091a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        broadcastAudioState(mDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1092a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                                BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1093122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        break;
109469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    }
109567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogI("processAudioEvent: audio connected");
1096122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mAudioOn);
109769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
1098122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1099a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                    if (!mHeadsetService.isScoAcceptable(mDevice)) {
110067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogW("processAudioEvent: reject incoming pending audio connection");
110167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        if (!mNativeInterface.disconnectAudio(mDevice)) {
110267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            stateLogE("processAudioEvent: failed to disconnect pending audio");
110369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                        }
1104a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        // Indicate rejection to other components.
1105a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        broadcastAudioState(mDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1106a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                                BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
110739af3989003fd06fb3da55904ee074f706e1d448Jack He                        break;
110839af3989003fd06fb3da55904ee074f706e1d448Jack He                    }
110967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogI("processAudioEvent: audio connecting");
1110122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mAudioConnecting);
1111122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
1112122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1113122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1114122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // ignore
111569d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
111669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                default:
111767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogE("processAudioEvent: bad state: " + state);
111869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
111969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            }
1120122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1121122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    }
1122122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
1123122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    class AudioConnecting extends ConnectedBase {
1124122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1125122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getAudioStateInt() {
1126122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothHeadset.STATE_AUDIO_CONNECTING;
1127122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1128122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
1129122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1130122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public void enter() {
1131122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.enter();
113267ccceab9600aaeec6dc34658c45c074de39aa38Jack He            sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs);
1133122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            broadcastStateTransitions();
1134122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1135122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
1136122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1137122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public boolean processMessage(Message message) {
1138122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            switch (message.what) {
1139122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT:
1140122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT:
1141122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_AUDIO:
1142122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT_AUDIO:
1143122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    deferMessage(message);
1144122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
1145122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_TIMEOUT: {
1146122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
114767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
1148122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        stateLogW("CONNECT_TIMEOUT for unknown device " + device);
1149122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        break;
1150122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    }
1151122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogW("CONNECT_TIMEOUT");
1152122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mConnected);
1153122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
1154122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                }
1155122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                default:
1156122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    return super.processMessage(message);
115739af3989003fd06fb3da55904ee074f706e1d448Jack He            }
1158122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return HANDLED;
115969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        }
116069d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava
1161122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
116267ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public void processAudioEvent(int state) {
116369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            switch (state) {
116469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
116567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("processAudioEvent: audio connection failed");
116669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    transitionTo(mConnected);
116769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
1168122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1169122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // ignore, already in audio connecting state
1170122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
117169d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1172122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // ignore, there is no BluetoothHeadset.STATE_AUDIO_DISCONNECTING
1173122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
1174122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
117567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogI("processAudioEvent: audio connected");
1176122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mAudioOn);
117769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
117869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                default:
117967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogE("processAudioEvent: bad state: " + state);
118069d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
118169d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            }
1182122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
118369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava
1184122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1185122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public void exit() {
1186122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            removeMessages(CONNECT_TIMEOUT);
1187122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.exit();
118869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        }
118969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    }
119069d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava
1191122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    class AudioOn extends ConnectedBase {
1192122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1193122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getAudioStateInt() {
1194122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothHeadset.STATE_AUDIO_CONNECTED;
1195122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1196122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
119769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        @Override
119869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        public void enter() {
1199122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.enter();
1200122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            removeDeferredMessages(CONNECT_AUDIO);
120167ccceab9600aaeec6dc34658c45c074de39aa38Jack He            // Set active device to current active SCO device when the current active device
120267ccceab9600aaeec6dc34658c45c074de39aa38Jack He            // is different from mCurrentDevice. This is to accommodate active device state
120367ccceab9600aaeec6dc34658c45c074de39aa38Jack He            // mis-match between native and Java.
1204caa5d6ab09bf61f4379413299ae1ab436c503710Jack He            if (!mDevice.equals(mHeadsetService.getActiveDevice())) {
1205caa5d6ab09bf61f4379413299ae1ab436c503710Jack He                mHeadsetService.setActiveDevice(mDevice);
120667ccceab9600aaeec6dc34658c45c074de39aa38Jack He            }
120767ccceab9600aaeec6dc34658c45c074de39aa38Jack He            setAudioParameters();
1208122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            broadcastStateTransitions();
120969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        }
121069d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava
121169d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        @Override
121269d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        public boolean processMessage(Message message) {
12133659dee2b21c7f269a2bef051483093fe07e5682Jack He            switch (message.what) {
1214122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT: {
1215122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
121667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("CONNECT, ignored, device=" + device + ", currentDevice" + mDevice);
121769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
1218122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                }
1219122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT: {
1220122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
1221122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("DISCONNECT, device=" + device);
122267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
1223122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        stateLogW("DISCONNECT, device " + device + " not connected");
1224122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        break;
122569d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    }
1226122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // Disconnect BT SCO first
122767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mNativeInterface.disconnectAudio(mDevice)) {
122867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogW("DISCONNECT failed, device=" + mDevice);
1229122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // if disconnect BT SCO failed, transition to mConnected state to force
1230122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        // disconnect device
123169d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    }
123267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    deferMessage(obtainMessage(DISCONNECT, mDevice));
123367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    transitionTo(mAudioDisconnecting);
123469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
1235122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                }
1236122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_AUDIO: {
123769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    BluetoothDevice device = (BluetoothDevice) message.obj;
123867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
1239122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        stateLogW("CONNECT_AUDIO device is not connected " + device);
1240122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        break;
1241122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    }
1242122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogW("CONNECT_AUDIO device auido is already connected " + device);
1243122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
1244122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                }
124567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                case DISCONNECT_AUDIO: {
124667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
124767ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
124867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogW("DISCONNECT_AUDIO, failed, device=" + device + ", currentDevice="
124967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + mDevice);
125067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
125167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
125267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (mNativeInterface.disconnectAudio(mDevice)) {
125367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogD("DISCONNECT_AUDIO, device=" + mDevice);
1254122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        transitionTo(mAudioDisconnecting);
125569d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    } else {
125667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogW("DISCONNECT_AUDIO failed, device=" + mDevice);
1257a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                        broadcastAudioState(mDevice, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1258a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                                BluetoothHeadset.STATE_AUDIO_CONNECTED);
12596c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    }
12606c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
1261122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                }
126267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                case INTENT_SCO_VOLUME_CHANGED:
126367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    processIntentScoVolume((Intent) message.obj, mDevice);
1264b18d6a7da48a6847df88abadd42fbb51316cb76dRavi Nagarajan                    break;
12656c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                case STACK_EVENT:
12660420b4fe3c7794139218821fab49f7f149d0075eJack He                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
1267122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogD("STACK_EVENT: " + event);
126867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(event.device)) {
126967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        stateLogE("Event device does not match currentDevice[" + mDevice
127067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                                + "], event: " + event);
127167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        break;
127267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    }
12736c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    switch (event.type) {
127467ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        case HeadsetStackEvent.EVENT_TYPE_WBS:
127567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            stateLogE("Cannot change WBS state when audio is connected: " + event);
12762ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                            break;
12776c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                        default:
127867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                            super.processMessage(message);
12796c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                            break;
12806c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    }
12816c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
12826c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                default:
128367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    return super.processMessage(message);
12846c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
1285c6231925ec13a432770a879c49b3c4f008ee96e1Jack He            return HANDLED;
12866c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
12876c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1288122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
128967ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public void processAudioEvent(int state) {
129042a6e53f2ec04cbd78fea952fc926fdf3e5b8cd9Ravi Nagarajan            switch (state) {
1291122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
129267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogI("processAudioEvent: audio disconnected by remote");
1293122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mConnected);
129442a6e53f2ec04cbd78fea952fc926fdf3e5b8cd9Ravi Nagarajan                    break;
1295122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
129667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogI("processAudioEvent: audio being disconnected by remote");
1297122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mAudioDisconnecting);
129839af3989003fd06fb3da55904ee074f706e1d448Jack He                    break;
1299122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                default:
130067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogE("processAudioEvent: bad state: " + state);
13013659dee2b21c7f269a2bef051483093fe07e5682Jack He                    break;
1302122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
1303122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1304122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
1305122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
1306122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
130750b51beb3bd0885ced514d1bcfda3050f60833ffJack He            if (mSpeakerVolume != volumeValue) {
130850b51beb3bd0885ced514d1bcfda3050f60833ffJack He                mSpeakerVolume = volumeValue;
1309122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                mNativeInterface.setVolume(device, HeadsetHalConstants.VOLUME_TYPE_SPK,
131050b51beb3bd0885ced514d1bcfda3050f60833ffJack He                        mSpeakerVolume);
1311122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            }
1312122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1313122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    }
1314122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
1315122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    class AudioDisconnecting extends ConnectedBase {
1316122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1317122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int getAudioStateInt() {
1318122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            // TODO: need BluetoothHeadset.STATE_AUDIO_DISCONNECTING
1319122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return BluetoothHeadset.STATE_AUDIO_CONNECTED;
1320122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1321122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
1322122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1323122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public void enter() {
1324122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.enter();
132567ccceab9600aaeec6dc34658c45c074de39aa38Jack He            sendMessageDelayed(CONNECT_TIMEOUT, mDevice, sConnectTimeoutMs);
1326122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            broadcastStateTransitions();
1327122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
1328122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
1329122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1330122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public boolean processMessage(Message message) {
1331122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            switch (message.what) {
1332122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT:
1333122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT:
1334122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_AUDIO:
1335122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case DISCONNECT_AUDIO:
1336122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    deferMessage(message);
13373659dee2b21c7f269a2bef051483093fe07e5682Jack He                    break;
1338122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case CONNECT_TIMEOUT: {
1339122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    BluetoothDevice device = (BluetoothDevice) message.obj;
134067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    if (!mDevice.equals(device)) {
1341122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        stateLogW("CONNECT_TIMEOUT for unknown device " + device);
1342122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                        break;
134369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    }
1344122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    stateLogW("CONNECT_TIMEOUT");
1345122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mConnected);
13463659dee2b21c7f269a2bef051483093fe07e5682Jack He                    break;
1347122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                }
13483659dee2b21c7f269a2bef051483093fe07e5682Jack He                default:
1349122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    return super.processMessage(message);
135039af3989003fd06fb3da55904ee074f706e1d448Jack He            }
1351122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            return HANDLED;
135242a6e53f2ec04cbd78fea952fc926fdf3e5b8cd9Ravi Nagarajan        }
135342a6e53f2ec04cbd78fea952fc926fdf3e5b8cd9Ravi Nagarajan
1354122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
135567ccceab9600aaeec6dc34658c45c074de39aa38Jack He        public void processAudioEvent(int state) {
13566c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            switch (state) {
1357122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
135867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogI("processAudioEvent: audio disconnected");
1359122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mConnected);
1360122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
1361122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1362122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // ignore
1363122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    break;
136469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
136567ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogW("processAudioEvent: audio disconnection failed");
1366122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    transitionTo(mAudioOn);
136769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                    break;
136869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1369122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                    // ignore, see if it goes into connected state, otherwise, timeout
13706c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
13716c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                default:
137267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    stateLogE("processAudioEvent: bad state: " + state);
13736c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                    break;
13746c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
13756c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
13766c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1377122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        @Override
1378122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        public void exit() {
1379122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            removeMessages(CONNECT_TIMEOUT);
1380122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            super.exit();
1381c924fea547063adc907a3a25044588aa58ac8879Mallikarjuna GB        }
13826c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
13836c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
138467ccceab9600aaeec6dc34658c45c074de39aa38Jack He    /**
138567ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * Get the underlying device tracked by this state machine
138667ccceab9600aaeec6dc34658c45c074de39aa38Jack He     *
138767ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * @return device in focus
138867ccceab9600aaeec6dc34658c45c074de39aa38Jack He     */
138967ccceab9600aaeec6dc34658c45c074de39aa38Jack He    @VisibleForTesting
139067ccceab9600aaeec6dc34658c45c074de39aa38Jack He    public synchronized BluetoothDevice getDevice() {
139167ccceab9600aaeec6dc34658c45c074de39aa38Jack He        return mDevice;
1392122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    }
1393122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
139467ccceab9600aaeec6dc34658c45c074de39aa38Jack He    /**
139567ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * Get the current connection state of this state machine
139667ccceab9600aaeec6dc34658c45c074de39aa38Jack He     *
139767ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * @return current connection state, one of {@link BluetoothProfile#STATE_DISCONNECTED},
139867ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or
139967ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * {@link BluetoothProfile#STATE_DISCONNECTING}
140067ccceab9600aaeec6dc34658c45c074de39aa38Jack He     */
140167ccceab9600aaeec6dc34658c45c074de39aa38Jack He    @VisibleForTesting
140267ccceab9600aaeec6dc34658c45c074de39aa38Jack He    public synchronized int getConnectionState() {
140367ccceab9600aaeec6dc34658c45c074de39aa38Jack He        HeadsetStateBase state = (HeadsetStateBase) getCurrentState();
140467ccceab9600aaeec6dc34658c45c074de39aa38Jack He        if (state == null) {
140567ccceab9600aaeec6dc34658c45c074de39aa38Jack He            return BluetoothHeadset.STATE_DISCONNECTED;
14066c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
140767ccceab9600aaeec6dc34658c45c074de39aa38Jack He        return state.getConnectionStateInt();
14086c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
14096c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
141067ccceab9600aaeec6dc34658c45c074de39aa38Jack He    /**
141167ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * Get the current audio state of this state machine
141267ccceab9600aaeec6dc34658c45c074de39aa38Jack He     *
141367ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * @return current audio state, one of {@link BluetoothHeadset#STATE_AUDIO_DISCONNECTED},
141467ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * {@link BluetoothHeadset#STATE_AUDIO_CONNECTING}, or
141567ccceab9600aaeec6dc34658c45c074de39aa38Jack He     * {@link BluetoothHeadset#STATE_AUDIO_CONNECTED}
141667ccceab9600aaeec6dc34658c45c074de39aa38Jack He     */
141767ccceab9600aaeec6dc34658c45c074de39aa38Jack He    public synchronized int getAudioState() {
141867ccceab9600aaeec6dc34658c45c074de39aa38Jack He        HeadsetStateBase state = (HeadsetStateBase) getCurrentState();
141967ccceab9600aaeec6dc34658c45c074de39aa38Jack He        if (state == null) {
142067ccceab9600aaeec6dc34658c45c074de39aa38Jack He            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
14216c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
142267ccceab9600aaeec6dc34658c45c074de39aa38Jack He        return state.getAudioStateInt();
14236c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
14246c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
142567ccceab9600aaeec6dc34658c45c074de39aa38Jack He    public long getConnectingTimestampMs() {
142667ccceab9600aaeec6dc34658c45c074de39aa38Jack He        return mConnectingTimestampMs;
142769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    }
142869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava
1429dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu    /*
1430dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
1431dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     */
14323659dee2b21c7f269a2bef051483093fe07e5682Jack He    private void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType,
14333659dee2b21c7f269a2bef051483093fe07e5682Jack He            Object[] arguments, BluetoothDevice device) {
1434dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        log("broadcastVendorSpecificEventIntent(" + command + ")");
14353659dee2b21c7f269a2bef051483093fe07e5682Jack He        Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
1436dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
14373659dee2b21c7f269a2bef051483093fe07e5682Jack He        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType);
1438dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        // assert: all elements of args are Serializable
1439dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
1440dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
14413659dee2b21c7f269a2bef051483093fe07e5682Jack He        intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
14423659dee2b21c7f269a2bef051483093fe07e5682Jack He                + Integer.toString(companyId));
1443caa5d6ab09bf61f4379413299ae1ab436c503710Jack He        mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
1444dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu    }
1445dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu
144667ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private void setAudioParameters() {
144767ccceab9600aaeec6dc34658c45c074de39aa38Jack He        String keyValuePairs = String.join(";", new String[]{
144867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                HEADSET_NAME + "=" + getCurrentDeviceName(),
144967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                HEADSET_NREC + "=" + mAudioParams.getOrDefault(HEADSET_NREC,
145067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        HEADSET_AUDIO_FEATURE_OFF),
145167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                HEADSET_WBS + "=" + mAudioParams.getOrDefault(HEADSET_WBS,
145267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                        HEADSET_AUDIO_FEATURE_OFF)
145367ccceab9600aaeec6dc34658c45c074de39aa38Jack He        });
145467ccceab9600aaeec6dc34658c45c074de39aa38Jack He        Log.i(TAG, "setAudioParameters for " + mDevice + ": " + keyValuePairs);
145567ccceab9600aaeec6dc34658c45c074de39aa38Jack He        mSystemInterface.getAudioManager().setParameters(keyValuePairs);
145668e7fc4f2c62ebc76e86109a919592ad25ec11d7Ravi Nagarajan    }
145768e7fc4f2c62ebc76e86109a919592ad25ec11d7Ravi Nagarajan
14583659dee2b21c7f269a2bef051483093fe07e5682Jack He    private String parseUnknownAt(String atString) {
1459405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        StringBuilder atCommand = new StringBuilder(atString.length());
1460405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T
1461405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        for (int i = 0; i < atString.length(); i++) {
1462405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            char c = atString.charAt(i);
1463405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            if (c == '"') {
14643659dee2b21c7f269a2bef051483093fe07e5682Jack He                int j = atString.indexOf('"', i + 1); // search for closing "
14653659dee2b21c7f269a2bef051483093fe07e5682Jack He                if (j == -1) { // unmatched ", insert one.
1466405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                    atCommand.append(atString.substring(i, atString.length()));
1467405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                    atCommand.append('"');
1468405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                    break;
1469405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                }
1470405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                atCommand.append(atString.substring(i, j + 1));
1471405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                i = j;
1472405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            } else if (c != ' ') {
1473405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                atCommand.append(Character.toUpperCase(c));
1474405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            }
1475405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        }
147667ccceab9600aaeec6dc34658c45c074de39aa38Jack He        return atCommand.toString();
1477405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T    }
1478405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T
14793659dee2b21c7f269a2bef051483093fe07e5682Jack He    private int getAtCommandType(String atCommand) {
1480122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        int commandType = AtPhonebook.TYPE_UNKNOWN;
1481405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        String atString = null;
1482405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        atCommand = atCommand.trim();
14833659dee2b21c7f269a2bef051483093fe07e5682Jack He        if (atCommand.length() > 5) {
1484405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            atString = atCommand.substring(5);
148576a12d7e679ae48f8ab73e01b33fb7c8f077a210Jack He            if (atString.startsWith("?")) { // Read
1486122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                commandType = AtPhonebook.TYPE_READ;
148776a12d7e679ae48f8ab73e01b33fb7c8f077a210Jack He            } else if (atString.startsWith("=?")) { // Test
1488122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                commandType = AtPhonebook.TYPE_TEST;
148976a12d7e679ae48f8ab73e01b33fb7c8f077a210Jack He            } else if (atString.startsWith("=")) { // Set
1490122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                commandType = AtPhonebook.TYPE_SET;
149176a12d7e679ae48f8ab73e01b33fb7c8f077a210Jack He            } else {
1492122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                commandType = AtPhonebook.TYPE_UNKNOWN;
149376a12d7e679ae48f8ab73e01b33fb7c8f077a210Jack He            }
1494405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        }
1495405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        return commandType;
1496405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T    }
1497405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T
1498a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    private void processDialCall(String number) {
14996c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        String dialNumber;
1500a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        if (mHeadsetService.hasDeviceInitiatedDialingOut()) {
1501a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Log.w(TAG, "processDialCall, already dialling");
1502a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
15038e79653c4d093d6f6696312f668429feb6d62bf3Mallikarjuna GB            return;
15048e79653c4d093d6f6696312f668429feb6d62bf3Mallikarjuna GB        }
15058f8e1bdc4dfc5d9d974b0e0b01dbe981707c9c6fRavi Nagarajan        if ((number == null) || (number.length() == 0)) {
15066c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            dialNumber = mPhonebook.getLastDialledNumber();
15076c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            if (dialNumber == null) {
1508a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                Log.w(TAG, "processDialCall, last dial number null");
1509a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
15106c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                return;
15116c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
15126c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        } else if (number.charAt(0) == '>') {
15136c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            // Yuck - memory dialling requested.
15146c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            // Just dial last number for now
15153659dee2b21c7f269a2bef051483093fe07e5682Jack He            if (number.startsWith(">9999")) { // for PTS test
1516a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                Log.w(TAG, "Number is too big");
1517a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
15186c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                return;
15196c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
1520c6231925ec13a432770a879c49b3c4f008ee96e1Jack He            log("processDialCall, memory dial do last dial for now");
15216c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            dialNumber = mPhonebook.getLastDialledNumber();
15229b300c1a33952a22c3bba1521da2d01cbf607b7bMatthew Xie            if (dialNumber == null) {
1523a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                Log.w(TAG, "processDialCall, last dial number null");
1524a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
15259b300c1a33952a22c3bba1521da2d01cbf607b7bMatthew Xie                return;
15269b300c1a33952a22c3bba1521da2d01cbf607b7bMatthew Xie            }
15276c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        } else {
15288f8e1bdc4dfc5d9d974b0e0b01dbe981707c9c6fRavi Nagarajan            // Remove trailing ';'
15298f8e1bdc4dfc5d9d974b0e0b01dbe981707c9c6fRavi Nagarajan            if (number.charAt(number.length() - 1) == ';') {
15308f8e1bdc4dfc5d9d974b0e0b01dbe981707c9c6fRavi Nagarajan                number = number.substring(0, number.length() - 1);
15318f8e1bdc4dfc5d9d974b0e0b01dbe981707c9c6fRavi Nagarajan            }
15326c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            dialNumber = PhoneNumberUtils.convertPreDial(number);
15336c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
1534a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        if (!mHeadsetService.dialOutgoingCall(mDevice, dialNumber)) {
1535a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Log.w(TAG, "processDialCall, failed to dial in service");
1536a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1537a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            return;
1538a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        }
1539a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        mNeedDialingOutReply = true;
15406c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
15416c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1542ff5993924ef627e8b723d33ccb5514cb15bb134dJack He    private void processVrEvent(int state) {
1543ff5993924ef627e8b723d33ccb5514cb15bb134dJack He        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
1544ff5993924ef627e8b723d33ccb5514cb15bb134dJack He            if (!mHeadsetService.startVoiceRecognitionByHeadset(mDevice)) {
1545ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1546ff5993924ef627e8b723d33ccb5514cb15bb134dJack He            }
1547ff5993924ef627e8b723d33ccb5514cb15bb134dJack He        } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
1548ff5993924ef627e8b723d33ccb5514cb15bb134dJack He            if (mHeadsetService.stopVoiceRecognitionByHeadset(mDevice)) {
1549ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0);
1550ff5993924ef627e8b723d33ccb5514cb15bb134dJack He            } else {
1551ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1552ff5993924ef627e8b723d33ccb5514cb15bb134dJack He            }
1553ff5993924ef627e8b723d33ccb5514cb15bb134dJack He        } else {
1554ff5993924ef627e8b723d33ccb5514cb15bb134dJack He            mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1555ff5993924ef627e8b723d33ccb5514cb15bb134dJack He        }
1556ff5993924ef627e8b723d33ccb5514cb15bb134dJack He    }
1557ff5993924ef627e8b723d33ccb5514cb15bb134dJack He
155867ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private void processVolumeEvent(int volumeType, int volume) {
1559dbd53d223fddcad41820c3ded5d36058c5910e7fJack He        // Only current active device can change SCO volume
1560dbd53d223fddcad41820c3ded5d36058c5910e7fJack He        if (!mDevice.equals(mHeadsetService.getActiveDevice())) {
1561dbd53d223fddcad41820c3ded5d36058c5910e7fJack He            Log.w(TAG, "processVolumeEvent, ignored because " + mDevice + " is not active");
1562dbd53d223fddcad41820c3ded5d36058c5910e7fJack He            return;
156350b51beb3bd0885ced514d1bcfda3050f60833ffJack He        }
15646c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
156550b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mSpeakerVolume = volume;
15666c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
156750b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mSystemInterface.getAudioManager()
156850b51beb3bd0885ced514d1bcfda3050f60833ffJack He                    .setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
1569f0b6639617ce2245ffb88968e8a864d0fa99dd8cMatthew Xie        } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
157050b51beb3bd0885ced514d1bcfda3050f60833ffJack He            // Not used currently
157150b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mMicVolume = volume;
1572f0b6639617ce2245ffb88968e8a864d0fa99dd8cMatthew Xie        } else {
1573dbd53d223fddcad41820c3ded5d36058c5910e7fJack He            Log.e(TAG, "Bad volume type: " + volumeType);
1574f0b6639617ce2245ffb88968e8a864d0fa99dd8cMatthew Xie        }
15756c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
15766c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
157767ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private void processNoiseReductionEvent(boolean enable) {
157867ccceab9600aaeec6dc34658c45c074de39aa38Jack He        String prevNrec = mAudioParams.getOrDefault(HEADSET_NREC, HEADSET_AUDIO_FEATURE_OFF);
157967ccceab9600aaeec6dc34658c45c074de39aa38Jack He        String newNrec = enable ? HEADSET_AUDIO_FEATURE_ON : HEADSET_AUDIO_FEATURE_OFF;
158067ccceab9600aaeec6dc34658c45c074de39aa38Jack He        mAudioParams.put(HEADSET_NREC, newNrec);
158167ccceab9600aaeec6dc34658c45c074de39aa38Jack He        log("processNoiseReductionEvent: " + HEADSET_NREC + " change " + prevNrec + " -> "
158267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                + newNrec);
158367ccceab9600aaeec6dc34658c45c074de39aa38Jack He        if (getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
158467ccceab9600aaeec6dc34658c45c074de39aa38Jack He            setAudioParameters();
15856385f5ec823dd1113708532d20729dba67d9beaaSungmin Choi        }
158668e7fc4f2c62ebc76e86109a919592ad25ec11d7Ravi Nagarajan    }
158768e7fc4f2c62ebc76e86109a919592ad25ec11d7Ravi Nagarajan
1588122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    private void processWBSEvent(int wbsConfig) {
158967ccceab9600aaeec6dc34658c45c074de39aa38Jack He        String prevWbs = mAudioParams.getOrDefault(HEADSET_WBS, HEADSET_AUDIO_FEATURE_OFF);
1590122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        switch (wbsConfig) {
1591122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case HeadsetHalConstants.BTHF_WBS_YES:
159267ccceab9600aaeec6dc34658c45c074de39aa38Jack He                mAudioParams.put(HEADSET_WBS, HEADSET_AUDIO_FEATURE_ON);
1593122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                break;
1594122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case HeadsetHalConstants.BTHF_WBS_NO:
1595122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case HeadsetHalConstants.BTHF_WBS_NONE:
159667ccceab9600aaeec6dc34658c45c074de39aa38Jack He                mAudioParams.put(HEADSET_WBS, HEADSET_AUDIO_FEATURE_OFF);
1597122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                break;
1598122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            default:
159967ccceab9600aaeec6dc34658c45c074de39aa38Jack He                Log.e(TAG, "processWBSEvent: unknown wbsConfig " + wbsConfig);
160067ccceab9600aaeec6dc34658c45c074de39aa38Jack He                return;
1601b6132733b6e386cc8c93b5598c72ed8efe04bf3eMatthew Xie        }
160267ccceab9600aaeec6dc34658c45c074de39aa38Jack He        log("processWBSEvent: " + HEADSET_NREC + " change " + prevWbs + " -> " + mAudioParams.get(
160367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                HEADSET_WBS));
1604b6132733b6e386cc8c93b5598c72ed8efe04bf3eMatthew Xie    }
1605b6132733b6e386cc8c93b5598c72ed8efe04bf3eMatthew Xie
160669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processAtChld(int chld, BluetoothDevice device) {
160750b51beb3bd0885ced514d1bcfda3050f60833ffJack He        if (mSystemInterface.processChld(chld)) {
160850b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
16096c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        } else {
16100420b4fe3c7794139218821fab49f7f149d0075eJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
16116c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
16126c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
16136c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
161469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processSubscriberNumberRequest(BluetoothDevice device) {
161550b51beb3bd0885ced514d1bcfda3050f60833ffJack He        String number = mSystemInterface.getSubscriberNumber();
161650b51beb3bd0885ced514d1bcfda3050f60833ffJack He        if (number != null) {
161750b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mNativeInterface.atResponseString(device,
161850b51beb3bd0885ced514d1bcfda3050f60833ffJack He                    "+CNUM: ,\"" + number + "\"," + PhoneNumberUtils.toaFromString(number) + ",,4");
161950b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
16206c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        } else {
162150b51beb3bd0885ced514d1bcfda3050f60833ffJack He            Log.e(TAG, "getSubscriberNumber returns null");
162250b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
16236c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
16246c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
16256c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
162669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processAtCind(BluetoothDevice device) {
16275ef52da260f8b4c1baed22bceb2983a694bb022cJack He        int call, callSetup;
162850b51beb3bd0885ced514d1bcfda3050f60833ffJack He        final HeadsetPhoneState phoneState = mSystemInterface.getHeadsetPhoneState();
162969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava
16306b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        /* Handsfree carkits expect that +CIND is properly responded to
16316b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M         Hence we ensure that a proper response is sent
16326b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M         for the virtual call too.*/
1633a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        if (mHeadsetService.isVirtualCallStarted()) {
16346b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M            call = 1;
16355ef52da260f8b4c1baed22bceb2983a694bb022cJack He            callSetup = 0;
16366b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        } else {
16376b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M            // regular phone call
163850b51beb3bd0885ced514d1bcfda3050f60833ffJack He            call = phoneState.getNumActiveCall();
163950b51beb3bd0885ced514d1bcfda3050f60833ffJack He            callSetup = phoneState.getNumHeldCall();
16406b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M        }
16416b19b0e94877ae8f0803133f8cfb4885acff2763Syed Ibrahim M
164250b51beb3bd0885ced514d1bcfda3050f60833ffJack He        mNativeInterface.cindResponse(device, phoneState.getCindService(), call, callSetup,
164350b51beb3bd0885ced514d1bcfda3050f60833ffJack He                phoneState.getCallState(), phoneState.getCindSignal(), phoneState.getCindRoam(),
164450b51beb3bd0885ced514d1bcfda3050f60833ffJack He                phoneState.getCindBatteryCharge());
16456c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
16466c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
164769d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processAtCops(BluetoothDevice device) {
164850b51beb3bd0885ced514d1bcfda3050f60833ffJack He        String operatorName = mSystemInterface.getNetworkOperator();
164950b51beb3bd0885ced514d1bcfda3050f60833ffJack He        if (operatorName == null) {
165050b51beb3bd0885ced514d1bcfda3050f60833ffJack He            operatorName = "";
16516c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
165250b51beb3bd0885ced514d1bcfda3050f60833ffJack He        mNativeInterface.copsResponse(device, operatorName);
16536c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
16546c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
165569d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processAtClcc(BluetoothDevice device) {
1656a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        if (mHeadsetService.isVirtualCallStarted()) {
165750b51beb3bd0885ced514d1bcfda3050f60833ffJack He            // In virtual call, send our phone number instead of remote phone number
165850b51beb3bd0885ced514d1bcfda3050f60833ffJack He            String phoneNumber = mSystemInterface.getSubscriberNumber();
165950b51beb3bd0885ced514d1bcfda3050f60833ffJack He            if (phoneNumber == null) {
166050b51beb3bd0885ced514d1bcfda3050f60833ffJack He                phoneNumber = "";
16616c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
166250b51beb3bd0885ced514d1bcfda3050f60833ffJack He            int type = PhoneNumberUtils.toaFromString(phoneNumber);
166350b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mNativeInterface.clccResponse(device, 1, 0, 0, 0, false, phoneNumber, type);
16640420b4fe3c7794139218821fab49f7f149d0075eJack He            mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0);
166550b51beb3bd0885ced514d1bcfda3050f60833ffJack He        } else {
166650b51beb3bd0885ced514d1bcfda3050f60833ffJack He            // In Telecom call, ask Telecom to send send remote phone number
166750b51beb3bd0885ced514d1bcfda3050f60833ffJack He            if (!mSystemInterface.listCurrentCalls()) {
166850b51beb3bd0885ced514d1bcfda3050f60833ffJack He                Log.e(TAG, "processAtClcc: failed to list current calls for " + device);
166950b51beb3bd0885ced514d1bcfda3050f60833ffJack He                mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0);
167050b51beb3bd0885ced514d1bcfda3050f60833ffJack He            } else {
167167ccceab9600aaeec6dc34658c45c074de39aa38Jack He                sendMessageDelayed(CLCC_RSP_TIMEOUT, device, CLCC_RSP_TIMEOUT_MS);
167250b51beb3bd0885ced514d1bcfda3050f60833ffJack He            }
16736c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
16746c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
16756c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
167669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processAtCscs(String atString, int type, BluetoothDevice device) {
16773659dee2b21c7f269a2bef051483093fe07e5682Jack He        log("processAtCscs - atString = " + atString);
16783659dee2b21c7f269a2bef051483093fe07e5682Jack He        if (mPhonebook != null) {
167969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            mPhonebook.handleCscsCommand(atString, type, device);
16803659dee2b21c7f269a2bef051483093fe07e5682Jack He        } else {
1681405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            Log.e(TAG, "Phonebook handle null for At+CSCS");
16820420b4fe3c7794139218821fab49f7f149d0075eJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1683405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        }
1684405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T    }
1685405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T
168669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processAtCpbs(String atString, int type, BluetoothDevice device) {
16873659dee2b21c7f269a2bef051483093fe07e5682Jack He        log("processAtCpbs - atString = " + atString);
16883659dee2b21c7f269a2bef051483093fe07e5682Jack He        if (mPhonebook != null) {
168969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            mPhonebook.handleCpbsCommand(atString, type, device);
16903659dee2b21c7f269a2bef051483093fe07e5682Jack He        } else {
1691405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            Log.e(TAG, "Phonebook handle null for At+CPBS");
16920420b4fe3c7794139218821fab49f7f149d0075eJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1693405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        }
1694405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T    }
1695405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T
169669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processAtCpbr(String atString, int type, BluetoothDevice device) {
16973659dee2b21c7f269a2bef051483093fe07e5682Jack He        log("processAtCpbr - atString = " + atString);
16983659dee2b21c7f269a2bef051483093fe07e5682Jack He        if (mPhonebook != null) {
169969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            mPhonebook.handleCpbrCommand(atString, type, device);
17003659dee2b21c7f269a2bef051483093fe07e5682Jack He        } else {
1701405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T            Log.e(TAG, "Phonebook handle null for At+CPBR");
17020420b4fe3c7794139218821fab49f7f149d0075eJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1703405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        }
1704405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T    }
1705405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T
1706dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu    /**
1707dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     * Find a character ch, ignoring quoted sections.
1708dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     * Return input.length() if not found.
1709dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     */
1710dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private static int findChar(char ch, String input, int fromIndex) {
1711dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        for (int i = fromIndex; i < input.length(); i++) {
1712dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            char c = input.charAt(i);
1713dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            if (c == '"') {
1714dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu                i = input.indexOf('"', i + 1);
1715dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu                if (i == -1) {
1716dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu                    return input.length();
1717dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu                }
1718dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            } else if (c == ch) {
1719dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu                return i;
1720dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            }
1721dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        }
1722dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        return input.length();
1723dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu    }
1724dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu
1725dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu    /**
1726dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     * Break an argument string into individual arguments (comma delimited).
1727dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     * Integer arguments are turned into Integer objects. Otherwise a String
1728dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     * object is used.
1729dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu     */
1730dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    private static Object[] generateArgs(String input) {
1731dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        int i = 0;
1732dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        int j;
1733dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        ArrayList<Object> out = new ArrayList<Object>();
1734dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        while (i <= input.length()) {
1735dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            j = findChar(',', input, i);
1736dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu
1737dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            String arg = input.substring(i, j);
1738dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            try {
1739dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu                out.add(new Integer(arg));
1740dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            } catch (NumberFormatException e) {
1741dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu                out.add(arg);
1742dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            }
1743dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu
1744dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu            i = j + 1; // move past comma
1745dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        }
1746dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        return out.toArray();
1747dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu    }
1748dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu
1749cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee    /**
1750f27d360b7ec35eaddee66cac173c51c18c238b03Jack He     * Process vendor specific AT commands
1751122e077f24750ee0e7fd650cbfa832edeb216d07Jack He     *
1752f27d360b7ec35eaddee66cac173c51c18c238b03Jack He     * @param atString AT command after the "AT+" prefix
1753f27d360b7ec35eaddee66cac173c51c18c238b03Jack He     * @param device Remote device that has sent this command
1754cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee     */
1755f27d360b7ec35eaddee66cac173c51c18c238b03Jack He    private void processVendorSpecificAt(String atString, BluetoothDevice device) {
1756cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        log("processVendorSpecificAt - atString = " + atString);
1757cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee
1758cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        // Currently we accept only SET type commands.
1759cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        int indexOfEqual = atString.indexOf("=");
1760cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        if (indexOfEqual == -1) {
1761a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Log.w(TAG, "processVendorSpecificAt: command type error in " + atString);
17620420b4fe3c7794139218821fab49f7f149d0075eJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1763f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            return;
1764dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        }
1765cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee
1766cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        String command = atString.substring(0, indexOfEqual);
1767cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
1768cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        if (companyId == null) {
1769a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Log.i(TAG, "processVendorSpecificAt: unsupported command: " + atString);
17700420b4fe3c7794139218821fab49f7f149d0075eJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1771f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            return;
1772dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu        }
1773cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee
1774cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        String arg = atString.substring(indexOfEqual + 1);
1775cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        if (arg.startsWith("?")) {
1776a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Log.w(TAG, "processVendorSpecificAt: command type error in " + atString);
17770420b4fe3c7794139218821fab49f7f149d0075eJack He            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1778f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            return;
1779cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        }
1780cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee
1781cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        Object[] args = generateArgs(arg);
1782f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        if (command.equals(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL)) {
1783f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            processAtXapl(args, device);
1784f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        }
1785c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        broadcastVendorSpecificEventIntent(command, companyId, BluetoothHeadset.AT_CMD_TYPE_SET,
1786c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                args, device);
17870420b4fe3c7794139218821fab49f7f149d0075eJack He        mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
1788f27d360b7ec35eaddee66cac173c51c18c238b03Jack He    }
1789f27d360b7ec35eaddee66cac173c51c18c238b03Jack He
1790f27d360b7ec35eaddee66cac173c51c18c238b03Jack He    /**
1791f27d360b7ec35eaddee66cac173c51c18c238b03Jack He     * Process AT+XAPL AT command
1792122e077f24750ee0e7fd650cbfa832edeb216d07Jack He     *
1793f27d360b7ec35eaddee66cac173c51c18c238b03Jack He     * @param args command arguments after the equal sign
1794f27d360b7ec35eaddee66cac173c51c18c238b03Jack He     * @param device Remote device that has sent this command
1795f27d360b7ec35eaddee66cac173c51c18c238b03Jack He     */
1796f27d360b7ec35eaddee66cac173c51c18c238b03Jack He    private void processAtXapl(Object[] args, BluetoothDevice device) {
1797f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        if (args.length != 2) {
1798f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            Log.w(TAG, "processAtXapl() args length must be 2: " + String.valueOf(args.length));
1799f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            return;
1800f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        }
1801f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        if (!(args[0] instanceof String) || !(args[1] instanceof Integer)) {
1802f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            Log.w(TAG, "processAtXapl() argument types not match");
1803f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            return;
1804f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        }
1805f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        // feature = 2 indicates that we support battery level reporting only
18060420b4fe3c7794139218821fab49f7f149d0075eJack He        mNativeInterface.atResponseString(device, "+XAPL=iPhone," + String.valueOf(2));
1807dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu    }
1808dd66c701616d77e5bf7f5fd795999f59e5fe28bdZhihai Xu
180969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processUnknownAt(String atString, BluetoothDevice device) {
18103659dee2b21c7f269a2bef051483093fe07e5682Jack He        if (device == null) {
181169d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            Log.w(TAG, "processUnknownAt device is null");
181269d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            return;
181369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        }
18143659dee2b21c7f269a2bef051483093fe07e5682Jack He        log("processUnknownAt - atString = " + atString);
1815405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        String atCommand = parseUnknownAt(atString);
1816405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        int commandType = getAtCommandType(atCommand);
1817f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        if (atCommand.startsWith("+CSCS")) {
181869d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            processAtCscs(atCommand.substring(5), commandType, device);
1819f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        } else if (atCommand.startsWith("+CPBS")) {
182069d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            processAtCpbs(atCommand.substring(5), commandType, device);
1821f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        } else if (atCommand.startsWith("+CPBR")) {
182269d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            processAtCpbr(atCommand.substring(5), commandType, device);
1823f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        } else {
1824f27d360b7ec35eaddee66cac173c51c18c238b03Jack He            processVendorSpecificAt(atCommand, device);
1825f27d360b7ec35eaddee66cac173c51c18c238b03Jack He        }
18266c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
18276c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1828a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    // HSP +CKPD command
182969d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava    private void processKeyPressed(BluetoothDevice device) {
1830f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He        if (mSystemInterface.isRinging()) {
183150b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mSystemInterface.answerCall(device);
1832f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He        } else if (mSystemInterface.isInCall()) {
1833f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He            if (getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1834f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He                // Should connect audio as well
1835f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He                if (!mHeadsetService.setActiveDevice(mDevice)) {
1836f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He                    Log.w(TAG, "processKeyPressed, failed to set active device to " + mDevice);
183727fca676c2809f992575420cf9fe360ae909699bJack He                }
18383659dee2b21c7f269a2bef051483093fe07e5682Jack He            } else {
1839a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                mSystemInterface.hangupCall(device);
18406c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
1841f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He        } else if (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1842f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He            if (!mNativeInterface.disconnectAudio(mDevice)) {
1843f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He                Log.w(TAG, "processKeyPressed, failed to disconnect audio from " + mDevice);
1844f451ece4cd322f71ba3643c440e6b065e9e93f28Jack He            }
18456c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        } else {
1846a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            // We have already replied OK to this HSP command, no feedback is needed
1847a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            if (mHeadsetService.hasDeviceInitiatedDialingOut()) {
1848a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                Log.w(TAG, "processKeyPressed, already dialling");
1849a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                return;
1850a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            }
18516c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            String dialNumber = mPhonebook.getLastDialledNumber();
18526c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            if (dialNumber == null) {
1853a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                Log.w(TAG, "processKeyPressed, last dial number null");
1854a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                return;
1855a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            }
1856a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            if (!mHeadsetService.dialOutgoingCall(mDevice, dialNumber)) {
1857a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                Log.w(TAG, "processKeyPressed, failed to call in service");
18586c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie                return;
18596c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie            }
18606c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
18616c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
18626c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
18630b319f1df5ba13d6bec5a431cc4f329b43dd37fbJack He    /**
18640b319f1df5ba13d6bec5a431cc4f329b43dd37fbJack He     * Send HF indicator value changed intent
1865122e077f24750ee0e7fd650cbfa832edeb216d07Jack He     *
18660b319f1df5ba13d6bec5a431cc4f329b43dd37fbJack He     * @param device Device whose HF indicator value has changed
1867b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He     * @param indId Indicator ID [0-65535]
1868b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He     * @param indValue Indicator Value [0-65535], -1 means invalid but indId is supported
18690b319f1df5ba13d6bec5a431cc4f329b43dd37fbJack He     */
1870b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He    private void sendIndicatorIntent(BluetoothDevice device, int indId, int indValue) {
18712ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth        Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
18722ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1873b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He        intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId);
1874b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He        intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue);
18752ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
1876caa5d6ab09bf61f4379413299ae1ab436c503710Jack He        mHeadsetService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
18772ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth    }
18782ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
1879b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He    private void processAtBind(String atString, BluetoothDevice device) {
1880b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He        log("processAtBind: " + atString);
18812ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
18822ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth        // Parse the AT String to find the Indicator Ids that are supported
18835ef52da260f8b4c1baed22bceb2983a694bb022cJack He        int indId = 0;
18842ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth        int iter = 0;
18852ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth        int iter1 = 0;
18862ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
1887b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He        while (iter < atString.length()) {
1888b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He            iter1 = findChar(',', atString, iter);
1889b7461b17cad5476a7a528cbf2a0b9c9706c6faefJack He            String id = atString.substring(iter, iter1);
18902ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
18912ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth            try {
18925ef52da260f8b4c1baed22bceb2983a694bb022cJack He                indId = Integer.valueOf(id);
18932ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth            } catch (NumberFormatException e) {
18942ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                Log.e(TAG, Log.getStackTraceString(new Throwable()));
18952ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth            }
18962ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
18975ef52da260f8b4c1baed22bceb2983a694bb022cJack He            switch (indId) {
18983659dee2b21c7f269a2bef051483093fe07e5682Jack He                case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY:
1899c6231925ec13a432770a879c49b3c4f008ee96e1Jack He                    log("Send Broadcast intent for the Enhanced Driver Safety indicator.");
19005ef52da260f8b4c1baed22bceb2983a694bb022cJack He                    sendIndicatorIntent(device, indId, -1);
19012ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                    break;
19023659dee2b21c7f269a2bef051483093fe07e5682Jack He                case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS:
1903c6231925ec13a432770a879c49b3c4f008ee96e1Jack He                    log("Send Broadcast intent for the Battery Level indicator.");
19045ef52da260f8b4c1baed22bceb2983a694bb022cJack He                    sendIndicatorIntent(device, indId, -1);
19052ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                    break;
19062ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                default:
19072ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                    log("Invalid HF Indicator Received");
19082ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth                    break;
19092ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth            }
19102ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
19112ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth            iter = iter1 + 1; // move past comma
19122ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth        }
19132ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth    }
19142ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
1915c6231925ec13a432770a879c49b3c4f008ee96e1Jack He    private void processAtBiev(int indId, int indValue, BluetoothDevice device) {
1916c6231925ec13a432770a879c49b3c4f008ee96e1Jack He        log("processAtBiev: ind_id=" + indId + ", ind_value=" + indValue);
1917c6231925ec13a432770a879c49b3c4f008ee96e1Jack He        sendIndicatorIntent(device, indId, indValue);
19182ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth    }
19192ab50a41dac634a78cef60a419c3bcc848c1aa78Mudumba Ananth
19206c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    private void processSendClccResponse(HeadsetClccResponse clcc) {
192167ccceab9600aaeec6dc34658c45c074de39aa38Jack He        if (!hasMessages(CLCC_RSP_TIMEOUT)) {
19225d56b57b6fac07125f5462a6ed3dd3b1a546541bSunny Kapdi            return;
19235d56b57b6fac07125f5462a6ed3dd3b1a546541bSunny Kapdi        }
192469d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        if (clcc.mIndex == 0) {
192569d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava            removeMessages(CLCC_RSP_TIMEOUT);
192669d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        }
192767ccceab9600aaeec6dc34658c45c074de39aa38Jack He        mNativeInterface.clccResponse(mDevice, clcc.mIndex, clcc.mDirection, clcc.mStatus,
19280420b4fe3c7794139218821fab49f7f149d0075eJack He                clcc.mMode, clcc.mMpty, clcc.mNumber, clcc.mType);
19296c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
19306c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1931cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee    private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
1932cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        String stringToSend = resultCode.mCommand + ": ";
1933cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        if (resultCode.mArg != null) {
1934cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee            stringToSend += resultCode.mArg;
1935cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee        }
19360420b4fe3c7794139218821fab49f7f149d0075eJack He        mNativeInterface.atResponseString(resultCode.mDevice, stringToSend);
1937cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee    }
1938cdc8ec8d84c7f16b851238da0d80b51335c4ea07Edward Jee
193967ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private String getCurrentDeviceName() {
1940caa5d6ab09bf61f4379413299ae1ab436c503710Jack He        String deviceName = mAdapterService.getRemoteName(mDevice);
19416c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        if (deviceName == null) {
194267ccceab9600aaeec6dc34658c45c074de39aa38Jack He            return "<unknown>";
19436c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
19446c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        return deviceName;
19456c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
19466c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1947448309eada01c130b2fee8977f7fd74875978cbcJack He    private void updateAgIndicatorEnableState(
1948448309eada01c130b2fee8977f7fd74875978cbcJack He            HeadsetAgIndicatorEnableState agIndicatorEnableState) {
1949448309eada01c130b2fee8977f7fd74875978cbcJack He        if (Objects.equals(mAgIndicatorEnableState, agIndicatorEnableState)) {
1950448309eada01c130b2fee8977f7fd74875978cbcJack He            Log.i(TAG, "updateAgIndicatorEnableState, no change in indicator state "
1951448309eada01c130b2fee8977f7fd74875978cbcJack He                    + mAgIndicatorEnableState);
1952448309eada01c130b2fee8977f7fd74875978cbcJack He            return;
1953448309eada01c130b2fee8977f7fd74875978cbcJack He        }
1954448309eada01c130b2fee8977f7fd74875978cbcJack He        mAgIndicatorEnableState = agIndicatorEnableState;
1955448309eada01c130b2fee8977f7fd74875978cbcJack He        int events = PhoneStateListener.LISTEN_NONE;
1956448309eada01c130b2fee8977f7fd74875978cbcJack He        if (mAgIndicatorEnableState != null && mAgIndicatorEnableState.service) {
1957448309eada01c130b2fee8977f7fd74875978cbcJack He            events |= PhoneStateListener.LISTEN_SERVICE_STATE;
1958448309eada01c130b2fee8977f7fd74875978cbcJack He        }
1959448309eada01c130b2fee8977f7fd74875978cbcJack He        if (mAgIndicatorEnableState != null && mAgIndicatorEnableState.signal) {
1960448309eada01c130b2fee8977f7fd74875978cbcJack He            events |= PhoneStateListener.LISTEN_SIGNAL_STRENGTHS;
1961448309eada01c130b2fee8977f7fd74875978cbcJack He        }
1962448309eada01c130b2fee8977f7fd74875978cbcJack He        mSystemInterface.getHeadsetPhoneState().listenForPhoneState(mDevice, events);
1963448309eada01c130b2fee8977f7fd74875978cbcJack He    }
1964448309eada01c130b2fee8977f7fd74875978cbcJack He
1965522d3b2b0a69c0157ca87995d92bb712f6102c1aWink Saville    @Override
1966522d3b2b0a69c0157ca87995d92bb712f6102c1aWink Saville    protected void log(String msg) {
19676c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        if (DBG) {
1968522d3b2b0a69c0157ca87995d92bb712f6102c1aWink Saville            super.log(msg);
19696c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie        }
19706c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie    }
19716c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie
1972764073b18d2a0a44a021325db841a6b33e26241bJack He    @Override
1973764073b18d2a0a44a021325db841a6b33e26241bJack He    protected String getLogRecString(Message msg) {
1974764073b18d2a0a44a021325db841a6b33e26241bJack He        StringBuilder builder = new StringBuilder();
1975764073b18d2a0a44a021325db841a6b33e26241bJack He        builder.append(getMessageName(msg.what));
1976764073b18d2a0a44a021325db841a6b33e26241bJack He        builder.append(": ");
1977764073b18d2a0a44a021325db841a6b33e26241bJack He        builder.append("arg1=")
1978764073b18d2a0a44a021325db841a6b33e26241bJack He                .append(msg.arg1)
1979764073b18d2a0a44a021325db841a6b33e26241bJack He                .append(", arg2=")
1980764073b18d2a0a44a021325db841a6b33e26241bJack He                .append(msg.arg2)
1981764073b18d2a0a44a021325db841a6b33e26241bJack He                .append(", obj=");
1982764073b18d2a0a44a021325db841a6b33e26241bJack He        if (msg.obj instanceof HeadsetMessageObject) {
1983764073b18d2a0a44a021325db841a6b33e26241bJack He            HeadsetMessageObject object = (HeadsetMessageObject) msg.obj;
1984764073b18d2a0a44a021325db841a6b33e26241bJack He            object.buildString(builder);
1985764073b18d2a0a44a021325db841a6b33e26241bJack He        } else {
1986764073b18d2a0a44a021325db841a6b33e26241bJack He            builder.append(msg.obj);
1987764073b18d2a0a44a021325db841a6b33e26241bJack He        }
1988764073b18d2a0a44a021325db841a6b33e26241bJack He        return builder.toString();
1989764073b18d2a0a44a021325db841a6b33e26241bJack He    }
1990764073b18d2a0a44a021325db841a6b33e26241bJack He
199150b51beb3bd0885ced514d1bcfda3050f60833ffJack He    private void handleAccessPermissionResult(Intent intent) {
1992405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        log("handleAccessPermissionResult");
199369d4ca45a25e86823fbdb754ca6a3995f8131d59Nitin Srivastava        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
199450b51beb3bd0885ced514d1bcfda3050f60833ffJack He        if (!mPhonebook.getCheckingAccessPermission()) {
199550b51beb3bd0885ced514d1bcfda3050f60833ffJack He            return;
199650b51beb3bd0885ced514d1bcfda3050f60833ffJack He        }
199750b51beb3bd0885ced514d1bcfda3050f60833ffJack He        int atCommandResult = 0;
199850b51beb3bd0885ced514d1bcfda3050f60833ffJack He        int atCommandErrorCode = 0;
199950b51beb3bd0885ced514d1bcfda3050f60833ffJack He        // HeadsetBase headset = mHandsfree.getHeadset();
200050b51beb3bd0885ced514d1bcfda3050f60833ffJack He        // ASSERT: (headset != null) && headSet.isConnected()
200150b51beb3bd0885ced514d1bcfda3050f60833ffJack He        // REASON: mCheckingAccessPermission is true, otherwise resetAtState
200250b51beb3bd0885ced514d1bcfda3050f60833ffJack He        // has set mCheckingAccessPermission to false
200350b51beb3bd0885ced514d1bcfda3050f60833ffJack He        if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
200450b51beb3bd0885ced514d1bcfda3050f60833ffJack He            if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
200550b51beb3bd0885ced514d1bcfda3050f60833ffJack He                    BluetoothDevice.CONNECTION_ACCESS_NO)
200650b51beb3bd0885ced514d1bcfda3050f60833ffJack He                    == BluetoothDevice.CONNECTION_ACCESS_YES) {
200750b51beb3bd0885ced514d1bcfda3050f60833ffJack He                if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
200867ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
2009405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T                }
201050b51beb3bd0885ced514d1bcfda3050f60833ffJack He                atCommandResult = mPhonebook.processCpbrCommand(device);
2011fc9bed1b79edf77082f5baa21aa68d791a102ea4Edward Jee            } else {
201250b51beb3bd0885ced514d1bcfda3050f60833ffJack He                if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
201367ccceab9600aaeec6dc34658c45c074de39aa38Jack He                    mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
201450b51beb3bd0885ced514d1bcfda3050f60833ffJack He                }
2015fc9bed1b79edf77082f5baa21aa68d791a102ea4Edward Jee            }
201650b51beb3bd0885ced514d1bcfda3050f60833ffJack He        }
201750b51beb3bd0885ced514d1bcfda3050f60833ffJack He        mPhonebook.setCpbrIndex(-1);
201850b51beb3bd0885ced514d1bcfda3050f60833ffJack He        mPhonebook.setCheckingAccessPermission(false);
201950b51beb3bd0885ced514d1bcfda3050f60833ffJack He        if (atCommandResult >= 0) {
202050b51beb3bd0885ced514d1bcfda3050f60833ffJack He            mNativeInterface.atResponseCode(device, atCommandResult, atCommandErrorCode);
2021fc9bed1b79edf77082f5baa21aa68d791a102ea4Edward Jee        } else {
202250b51beb3bd0885ced514d1bcfda3050f60833ffJack He            log("handleAccessPermissionResult - RESULT_NONE");
2023405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T        }
2024405b6148a168c05f9c9eac6a80db68b9a58392e2Sreenidhi T    }
2025122e077f24750ee0e7fd650cbfa832edeb216d07Jack He
202667ccceab9600aaeec6dc34658c45c074de39aa38Jack He    private static String getMessageName(int what) {
2027122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        switch (what) {
2028122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case CONNECT:
2029122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "CONNECT";
2030122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case DISCONNECT:
2031122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "DISCONNECT";
2032122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case CONNECT_AUDIO:
2033122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "CONNECT_AUDIO";
2034122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case DISCONNECT_AUDIO:
2035122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "DISCONNECT_AUDIO";
2036122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case VOICE_RECOGNITION_START:
2037122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "VOICE_RECOGNITION_START";
2038122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case VOICE_RECOGNITION_STOP:
2039122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "VOICE_RECOGNITION_STOP";
2040122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case INTENT_SCO_VOLUME_CHANGED:
2041122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "INTENT_SCO_VOLUME_CHANGED";
204250b51beb3bd0885ced514d1bcfda3050f60833ffJack He            case INTENT_CONNECTION_ACCESS_REPLY:
204350b51beb3bd0885ced514d1bcfda3050f60833ffJack He                return "INTENT_CONNECTION_ACCESS_REPLY";
2044122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case CALL_STATE_CHANGED:
2045122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "CALL_STATE_CHANGED";
2046122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case DEVICE_STATE_CHANGED:
2047122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "DEVICE_STATE_CHANGED";
2048122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case SEND_CCLC_RESPONSE:
2049122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "SEND_CCLC_RESPONSE";
2050122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case SEND_VENDOR_SPECIFIC_RESULT_CODE:
2051122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "SEND_VENDOR_SPECIFIC_RESULT_CODE";
2052122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case STACK_EVENT:
2053122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "STACK_EVENT";
2054ff5993924ef627e8b723d33ccb5514cb15bb134dJack He            case VOICE_RECOGNITION_RESULT:
2055ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                return "VOICE_RECOGNITION_RESULT";
2056a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            case DIALING_OUT_RESULT:
2057a639b81f96c534c2f1066354b4a574c0dda2f713Jack He                return "DIALING_OUT_RESULT";
2058122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case CLCC_RSP_TIMEOUT:
2059122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "CLCC_RSP_TIMEOUT";
2060122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            case CONNECT_TIMEOUT:
2061122e077f24750ee0e7fd650cbfa832edeb216d07Jack He                return "CONNECT_TIMEOUT";
2062122e077f24750ee0e7fd650cbfa832edeb216d07Jack He            default:
2063ff5993924ef627e8b723d33ccb5514cb15bb134dJack He                return "UNKNOWN(" + what + ")";
2064122e077f24750ee0e7fd650cbfa832edeb216d07Jack He        }
2065122e077f24750ee0e7fd650cbfa832edeb216d07Jack He    }
20666c91bc0a163cc7600c40d7fb979777fd911d1ef1Matthew Xie}
2067