1b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project/*
2b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
3b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project *
4b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * you may not use this file except in compliance with the License.
6b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * You may obtain a copy of the License at
7b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project *
8b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project *
10b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * See the License for the specific language governing permissions and
14b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * limitations under the License.
15b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project */
16b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
17b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectpackage com.android.phone;
18b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
19b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.AtCommandHandler;
20b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.AtCommandResult;
21b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.AtParser;
22f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lanimport android.bluetooth.BluetoothA2dp;
231498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinekimport android.bluetooth.BluetoothAssignedNumbers;
24db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pellyimport android.bluetooth.BluetoothAdapter;
25b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.BluetoothDevice;
264079f559beb3e414036056b4b33ad40de5e89c4aThe Android Open Source Projectimport android.bluetooth.BluetoothHeadset;
271d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganeshimport android.bluetooth.BluetoothProfile;
2891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Changimport android.bluetooth.BluetoothServerSocket;
2991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Changimport android.bluetooth.BluetoothSocket;
30b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.bluetooth.HeadsetBase;
31b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.ActivityNotFoundException;
32b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.BroadcastReceiver;
33b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.Context;
34b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.Intent;
35b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.IntentFilter;
36b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.media.AudioManager;
37b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.net.Uri;
38b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.AsyncResult;
39b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.Bundle;
40b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.Handler;
41d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xieimport android.os.HandlerThread;
42d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xieimport android.os.Looper;
43b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.Message;
44b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.PowerManager;
45b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.PowerManager.WakeLock;
46b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.SystemProperties;
47b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.telephony.PhoneNumberUtils;
48b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.telephony.ServiceState;
49404edc94de563aef5fd5ba48be9114a970cb93bbWink Savilleimport android.telephony.SignalStrength;
50b0664bb07d16eef5a0dd70013ffd02ef6db3ebebRobert Greenwaltimport android.telephony.TelephonyManager;
51b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.util.Log;
52b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
53b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport com.android.internal.telephony.Call;
54b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport com.android.internal.telephony.Connection;
55b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport com.android.internal.telephony.Phone;
56b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport com.android.internal.telephony.TelephonyIntents;
578058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wangimport com.android.internal.telephony.CallManager;
58b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
5991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Changimport java.io.IOException;
6091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Changimport java.io.InputStream;
61b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport java.util.LinkedList;
62f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan
63b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project/**
64b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Bluetooth headset manager for the Phone app.
65b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * @hide
66b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project */
67b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectpublic class BluetoothHandsfree {
681d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private static final String TAG = "Bluetooth HS/HF";
698eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan    private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 1)
708eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan            && (SystemProperties.getInt("ro.debuggable", 0) == 1);
718eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan    private static final boolean VDBG = (PhoneApp.DBG_LEVEL >= 2);  // even more logging
72b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
73b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public static final int TYPE_UNKNOWN           = 0;
74b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public static final int TYPE_HEADSET           = 1;
75b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public static final int TYPE_HANDSFREE         = 2;
76b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
77f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown    /** The singleton instance. */
78f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown    private static BluetoothHandsfree sInstance;
79f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown
804079f559beb3e414036056b4b33ad40de5e89c4aThe Android Open Source Project    private final Context mContext;
8191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private final BluetoothAdapter mAdapter;
828058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang    private final CallManager mCM;
831d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private BluetoothA2dp mA2dp;
84f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan
85f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan    private BluetoothDevice mA2dpDevice;
86f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan    private int mA2dpState;
871d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private boolean mPendingAudioState;
881d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private int mAudioState;
89f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan
90b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private ServiceState mServiceState;
9190eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie    private HeadsetBase mHeadset;
921d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private BluetoothHeadset mBluetoothHeadset;
9390eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie    private int mHeadsetType;   // TYPE_UNKNOWN when not connected
94b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean mAudioPossible;
9591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private BluetoothSocket mConnectedSco;
9691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
9791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private IncomingScoAcceptThread mIncomingScoThread = null;
9891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private ScoSocketConnectThread mConnectScoThread = null;
9991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private SignalScoCloseThread mSignalScoCloseThread = null;
100b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
101b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private AudioManager mAudioManager;
102b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private PowerManager mPowerManager;
103b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
104f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan    private boolean mPendingSco;  // waiting for a2dp sink to suspend before establishing SCO
105ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent    private boolean mA2dpSuspended;
106b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean mUserWantsAudio;
107b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private WakeLock mStartCallWakeLock;  // held while waiting for the intent to start call
108b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private WakeLock mStartVoiceRecognitionWakeLock;  // held while waiting for voice recognition
109b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
110b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    // AT command state
111c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    private static final int GSM_MAX_CONNECTIONS = 6;  // Max connections allowed by GSM
112c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    private static final int CDMA_MAX_CONNECTIONS = 2;  // Max connections allowed by CDMA
113b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
114b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private long mBgndEarliestConnectionTime = 0;
115b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean mClip = false;  // Calling Line Information Presentation
116b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean mIndicatorsEnabled = false;
117b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean mCmee = false;  // Extended Error reporting
118b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private long[] mClccTimestamps; // Timestamps associated with each clcc index
119b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean[] mClccUsed;     // Is this clcc index in use
120b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean mWaitingForCallStart;
121b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean mWaitingForVoiceRecognition;
1220966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly    // do not connect audio until service connection is established
1230966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly    // for 3-way supported devices, this is after AT+CHLD
1240966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly    // for non-3-way supported devices, this is after AT+CMER (see spec)
1250966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly    private boolean mServiceConnectionEstablished;
126db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly
1271dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh    private final BluetoothPhoneState mBluetoothPhoneState;  // for CIND and CIEV updates
128b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private final BluetoothAtPhonebook mPhonebook;
1291dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh    private Phone.State mPhoneState = Phone.State.IDLE;
130487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh    CdmaPhoneCallState.PhoneCallState mCdmaThreeWayCallState =
131487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                            CdmaPhoneCallState.PhoneCallState.IDLE;
132b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
133b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private DebugThread mDebugThread;
134b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private int mScoGain = Integer.MIN_VALUE;
135b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
136b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static Intent sVoiceCommandIntent;
137b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
138b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    // Audio parameters
139b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final String HEADSET_NREC = "bt_headset_nrec";
140b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final String HEADSET_NAME = "bt_headset_name";
141b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
142b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private int mRemoteBrsf = 0;
143b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private int mLocalBrsf = 0;
144b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
145c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    // CDMA specific flag used in context with BT devices having display capabilities
146c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    // to show which Caller is active. This state might not be always true as in CDMA
147c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    // networks if a caller drops off no update is provided to the Phone.
148c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    // This flag is just used as a toggle to provide a update to the BT device to specify
149c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    // which caller is active.
150c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    private boolean mCdmaIsSecondCallActive = false;
15191f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh    private boolean mCdmaCallsSwapped = false;
152c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
153b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* Constants from Bluetooth Specification Hands-Free profile version 1.5 */
1546967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_THREE_WAY_CALLING = 1 << 0;
1556967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_EC_NR = 1 << 1;
1566967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_VOICE_RECOG = 1 << 2;
1576967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_IN_BAND_RING = 1 << 3;
1586967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_VOICE_TAG_NUMBE = 1 << 4;
1596967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_REJECT_CALL = 1 << 5;
1606967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_ENHANCED_CALL_STATUS = 1 <<  6;
1616967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_ENHANCED_CALL_CONTROL = 1 << 7;
1626967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_AG_ENHANCED_ERR_RESULT_CODES = 1 << 8;
1636967e2d953bc077c99c4831946201f3d333b833fNick Pelly
1646967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_HF_EC_NR = 1 << 0;
1656967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_HF_CW_THREE_WAY_CALLING = 1 << 1;
1666967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_HF_CLIP = 1 << 2;
1676967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_HF_VOICE_REG_ACT = 1 << 3;
1686967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_HF_REMOTE_VOL_CONTROL = 1 << 4;
1696967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_HF_ENHANCED_CALL_STATUS = 1 <<  5;
1706967e2d953bc077c99c4831946201f3d333b833fNick Pelly    private static final int BRSF_HF_ENHANCED_CALL_CONTROL = 1 << 6;
171b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
172b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    // VirtualCall - true if Virtual Call is active, false otherwise
173b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    private boolean mVirtualCallStarted = false;
174b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
1751c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent    // Voice Recognition - true if Voice Recognition is active, false otherwise
1761c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent    private boolean mVoiceRecognitionStarted;
1771c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
178d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie    private HandsfreeMessageHandler mHandler;
1791c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
180b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public static String typeToString(int type) {
181b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        switch (type) {
182b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case TYPE_UNKNOWN:
183b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return "unknown";
184b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case TYPE_HEADSET:
185b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return "headset";
186b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case TYPE_HANDSFREE:
187b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return "handsfree";
188b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
189b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return null;
190b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
191b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
192f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown    /**
193f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown     * Initialize the singleton BluetoothHandsfree instance.
194f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown     * This is only done once, at startup, from PhoneApp.onCreate().
195f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown     */
196f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown    /* package */ static BluetoothHandsfree init(Context context, CallManager cm) {
197f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown        synchronized (BluetoothHandsfree.class) {
198f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown            if (sInstance == null) {
199f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown                sInstance = new BluetoothHandsfree(context, cm);
200f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown            } else {
201f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown                Log.wtf(TAG, "init() called multiple times!  sInstance = " + sInstance);
202f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown            }
203f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown            return sInstance;
204f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown        }
205f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown    }
206f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown
207f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown    /** Private constructor; @see init() */
208f0c4d0e94b7fa915bcfacd28ee432f8b3846511aDavid Brown    private BluetoothHandsfree(Context context, CallManager cm) {
2098058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        mCM = cm;
210b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mContext = context;
21191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        mAdapter = BluetoothAdapter.getDefaultAdapter();
21291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        boolean bluetoothCapable = (mAdapter != null);
21390eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        mHeadset = null;
21490eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        mHeadsetType = TYPE_UNKNOWN; // nothing connected yet
2151d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        if (bluetoothCapable) {
2161d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            mAdapter.getProfileProxy(mContext, mProfileListener,
2171d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                                     BluetoothProfile.A2DP);
2181d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        }
219f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan        mA2dpState = BluetoothA2dp.STATE_DISCONNECTED;
220f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan        mA2dpDevice = null;
221ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent        mA2dpSuspended = false;
222b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
223b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
224b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mStartCallWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
225b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                                                       TAG + ":StartCall");
226b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mStartCallWakeLock.setReferenceCounted(false);
227b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
228b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                                                       TAG + ":VoiceRecognition");
229b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
230b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
231b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mLocalBrsf = BRSF_AG_THREE_WAY_CALLING |
232b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                     BRSF_AG_EC_NR |
233b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                     BRSF_AG_REJECT_CALL |
234b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                     BRSF_AG_ENHANCED_CALL_STATUS;
2354b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project
236b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (sVoiceCommandIntent == null) {
237b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
238b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
239b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2406967e2d953bc077c99c4831946201f3d333b833fNick Pelly        if (mContext.getPackageManager().resolveActivity(sVoiceCommandIntent, 0) != null &&
241b8dbab241df3aa3487c1bdb488fd4e0b694d2d9aEric Laurent                BluetoothHeadset.isBluetoothVoiceDialingEnabled(mContext)) {
242b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mLocalBrsf |= BRSF_AG_VOICE_RECOG;
243b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
244b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
245d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie        HandlerThread thread = new HandlerThread("BluetoothHandsfreeHandler");
246d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie        thread.start();
247d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie        Looper looper = thread.getLooper();
248d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie        mHandler = new HandsfreeMessageHandler(looper);
2491dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh        mBluetoothPhoneState = new BluetoothPhoneState();
250b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mUserWantsAudio = true;
251b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        mVirtualCallStarted = false;
2521c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        mVoiceRecognitionStarted = false;
253b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mPhonebook = new BluetoothAtPhonebook(mContext, this);
254b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
255c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        cdmaSetSecondCallState(false);
256f5945125c4e044b860f89a370fe376b97bd91d37Jaikumar Ganesh
257f5945125c4e044b860f89a370fe376b97bd91d37Jaikumar Ganesh        if (bluetoothCapable) {
258f5945125c4e044b860f89a370fe376b97bd91d37Jaikumar Ganesh            resetAtState();
259f5945125c4e044b860f89a370fe376b97bd91d37Jaikumar Ganesh        }
260f5945125c4e044b860f89a370fe376b97bd91d37Jaikumar Ganesh
261b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
262b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
26391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    /**
26491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     * A thread that runs in the background waiting for a Sco Server Socket to
26591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     * accept a connection. Even after a connection has been accepted, the Sco Server
26691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     * continues to listen for new connections.
26791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     */
26891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private class IncomingScoAcceptThread extends Thread{
26991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        private final BluetoothServerSocket mIncomingServerSocket;
27091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        private BluetoothSocket mIncomingSco;
27191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        private boolean stopped = false;
27291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
27391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        public IncomingScoAcceptThread() {
27491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            BluetoothServerSocket serverSocket = null;
27591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            try {
27691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                serverSocket = BluetoothAdapter.listenUsingScoOn();
27791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            } catch (IOException e) {
27891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                Log.e(TAG, "Could not create BluetoothServerSocket");
27991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                stopped = true;
28091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
28191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            mIncomingServerSocket = serverSocket;
28291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
28391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
28491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        @Override
28591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        public void run() {
28691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            while (!stopped) {
28791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                try {
28891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    mIncomingSco = mIncomingServerSocket.accept();
28991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                } catch (IOException e) {
29091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    Log.e(TAG, "BluetoothServerSocket could not accept connection");
29191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                }
29291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
29391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                if (mIncomingSco != null) {
294912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    connectSco();
295912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                }
296912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            }
297912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        }
298912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
299912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        private void connectSco() {
300e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            synchronized (BluetoothHandsfree.this) {
301e28e6bd39098d1af6459b27a6c423c43c906c5a2Eric Laurent                if (!Thread.interrupted() && isHeadsetConnected() &&
302e28e6bd39098d1af6459b27a6c423c43c906c5a2Eric Laurent                    (mAudioPossible || allowAudioAnytime()) &&
303912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    mConnectedSco == null) {
304912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    Log.i(TAG, "Routing audio for incoming SCO connection");
305912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    mConnectedSco = mIncomingSco;
306912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    mAudioManager.setBluetoothScoOn(true);
307912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    setAudioState(BluetoothHeadset.STATE_AUDIO_CONNECTED,
308912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mHeadset.getRemoteDevice());
309912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
310912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    if (mSignalScoCloseThread == null) {
311912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mSignalScoCloseThread = new SignalScoCloseThread();
312912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mSignalScoCloseThread.setName("SignalScoCloseThread");
313912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mSignalScoCloseThread.start();
314912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    }
315912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                } else {
316912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    Log.i(TAG, "Rejecting incoming SCO connection");
317912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    try {
318912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mIncomingSco.close();
319912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    }catch (IOException e) {
320912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        Log.e(TAG, "Error when closing incoming Sco socket");
32191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    }
322912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    mIncomingSco = null;
32391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                }
32491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
32591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
32691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
327e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent        // must be called with BluetoothHandsfree locked
32891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        void shutdown() {
32991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            try {
33091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                mIncomingServerSocket.close();
33191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            } catch (IOException e) {
33291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                Log.w(TAG, "Error when closing server socket");
33391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
334e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            stopped = true;
335e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            interrupt();
33691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
33791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    }
33891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
33991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    /**
34091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     * A thread that runs in the background waiting for a Sco Socket to
34191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     * connect.Once the socket is connected, this thread shall be
34291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     * shutdown.
34391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     */
34491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private class ScoSocketConnectThread extends Thread{
34591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        private BluetoothSocket mOutgoingSco;
34691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
34791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        public ScoSocketConnectThread(BluetoothDevice device) {
34891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            try {
34991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                mOutgoingSco = device.createScoSocket();
35091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            } catch (IOException e) {
35191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                Log.w(TAG, "Could not create BluetoothSocket");
352912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                failedScoConnect();
35391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
35491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
35591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
35691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        @Override
35791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        public void run() {
35891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            try {
35991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                mOutgoingSco.connect();
36091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }catch (IOException connectException) {
36191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                Log.e(TAG, "BluetoothSocket could not connect");
36291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                mOutgoingSco = null;
363912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                failedScoConnect();
364912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            }
365912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
366912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            if (mOutgoingSco != null) {
367912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                connectSco();
36891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
369912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        }
37091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
371912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        private void connectSco() {
372e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            synchronized (BluetoothHandsfree.this) {
373e28e6bd39098d1af6459b27a6c423c43c906c5a2Eric Laurent                if (!Thread.interrupted() && isHeadsetConnected() && mConnectedSco == null) {
374912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    if (VDBG) log("Routing audio for outgoing SCO conection");
375912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    mConnectedSco = mOutgoingSco;
376912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    mAudioManager.setBluetoothScoOn(true);
3771d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh
378912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    setAudioState(BluetoothHeadset.STATE_AUDIO_CONNECTED,
379912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                      mHeadset.getRemoteDevice());
3801d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh
381912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    if (mSignalScoCloseThread == null) {
382912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mSignalScoCloseThread = new SignalScoCloseThread();
383912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mSignalScoCloseThread.setName("SignalScoCloseThread");
384912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mSignalScoCloseThread.start();
385912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    }
386912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                } else {
387912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    if (VDBG) log("Rejecting new connected outgoing SCO socket");
388912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    try {
389912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        mOutgoingSco.close();
390912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    }catch (IOException e) {
391912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                        Log.e(TAG, "Error when closing Sco socket");
392912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    }
393912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    mOutgoingSco = null;
394912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh                    failedScoConnect();
39591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                }
39691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
39791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
39891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
399912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        private void failedScoConnect() {
400912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            // Wait for couple of secs before sending AUDIO_STATE_DISCONNECTED,
401912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            // since an incoming SCO connection can happen immediately with
402912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            // certain headsets.
403912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            Message msg = Message.obtain(mHandler, SCO_AUDIO_STATE);
404912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            msg.obj = mHeadset.getRemoteDevice();
405912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh            mHandler.sendMessageDelayed(msg, 2000);
40643d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie
40743d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            // Sync with interrupt() statement of shutdown method
40843d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            // This prevents resetting of a valid mConnectScoThread.
40943d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            // If this thread has been interrupted, it has been shutdown and
41043d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            // mConnectScoThread is/will be reset by the outer class.
41143d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            // We do not want to do it here since mConnectScoThread could be
41243d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            // assigned with a new object.
41343d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            synchronized (ScoSocketConnectThread.this) {
41443d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                if (!isInterrupted()) {
41543d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                    resetConnectScoThread();
41643d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                }
41743d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            }
418912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        }
419912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
420e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent        // must be called with BluetoothHandsfree locked
42191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        void shutdown() {
422e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            closeConnectedSco();
42343d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie
42443d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            // sync with isInterrupted() check in failedScoConnect method
42543d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            // see explanation there
42643d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            synchronized (ScoSocketConnectThread.this) {
42743d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                interrupt();
42843d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            }
42991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
43091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    }
43191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
43291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    /*
43391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     * Signals when a Sco connection has been closed
43491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang     */
43591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private class SignalScoCloseThread extends Thread{
43691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        private boolean stopped = false;
43791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
43891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        @Override
43991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        public void run() {
44091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            while (!stopped) {
441e28e6bd39098d1af6459b27a6c423c43c906c5a2Eric Laurent                BluetoothSocket connectedSco = null;
442e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent                synchronized (BluetoothHandsfree.this) {
443e28e6bd39098d1af6459b27a6c423c43c906c5a2Eric Laurent                    connectedSco = mConnectedSco;
444e28e6bd39098d1af6459b27a6c423c43c906c5a2Eric Laurent                }
445e28e6bd39098d1af6459b27a6c423c43c906c5a2Eric Laurent                if (connectedSco != null) {
44691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    byte b[] = new byte[1];
44791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    InputStream inStream = null;
44891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    try {
449e28e6bd39098d1af6459b27a6c423c43c906c5a2Eric Laurent                        inStream = connectedSco.getInputStream();
45091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    } catch (IOException e) {}
45191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
45291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    if (inStream != null) {
45391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                        try {
45491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            // inStream.read is a blocking call that won't ever
45591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            // return anything, but will throw an exception if the
45691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            // connection is closed
45791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            int ret = inStream.read(b, 0, 1);
45891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                        }catch (IOException connectException) {
45991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            // call a message to close this thread and turn off audio
46091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            // we can't call audioOff directly because then
46191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            // the thread would try to close itself
46291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            Message msg = Message.obtain(mHandler, SCO_CLOSED);
46391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            mHandler.sendMessage(msg);
46491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                            break;
46591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                        }
46691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    }
46791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                }
46891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
46991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
47091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
471e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent        // must be called with BluetoothHandsfree locked
47291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        void shutdown() {
473e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            stopped = true;
474e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            closeConnectedSco();
475e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            interrupt();
47691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
47791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    }
47891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
47991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private void connectScoThread(){
48043d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        // Sync with setting mConnectScoThread to null to assure the validity of
48143d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        // the condition
48243d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        synchronized (ScoSocketConnectThread.class) {
48343d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            if (mConnectScoThread == null) {
48443d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                BluetoothDevice device = mHeadset.getRemoteDevice();
48543d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                if (getAudioState(device) == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
48643d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                    setAudioState(BluetoothHeadset.STATE_AUDIO_CONNECTING, device);
48743d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                }
488912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
48943d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                mConnectScoThread = new ScoSocketConnectThread(mHeadset.getRemoteDevice());
49043d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                mConnectScoThread.setName("HandsfreeScoSocketConnectThread");
491912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
49243d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                mConnectScoThread.start();
49343d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            }
49443d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        }
49543d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie    }
49643d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie
49743d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie    private void resetConnectScoThread() {
49843d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        // Sync with if (mConnectScoThread == null) check
49943d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        synchronized (ScoSocketConnectThread.class) {
50043d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            mConnectScoThread = null;
50191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
50291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    }
50391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
504e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent    // must be called with BluetoothHandsfree locked
50591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    private void closeConnectedSco() {
50691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        if (mConnectedSco != null) {
50791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            try {
50891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                mConnectedSco.close();
50991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            } catch (IOException e) {
51091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                Log.e(TAG, "Error when closing Sco socket");
51191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
51291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
51391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            BluetoothDevice device = null;
51491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            if (mHeadset != null) {
51591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                device = mHeadset.getRemoteDevice();
51691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            }
51791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            mAudioManager.setBluetoothScoOn(false);
5181d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            setAudioState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, device);
51991b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
52091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            mConnectedSco = null;
52191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        }
52291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang    }
52391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
524b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ synchronized void onBluetoothEnabled() {
525b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /* Bluez has a bug where it will always accept and then orphan
526b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * incoming SCO connections, regardless of whether we have a listening
527b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * SCO socket. So the best thing to do is always run a listening socket
52891b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang         * while bluetooth is on so that at least we can disconnect it
529b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * immediately when we don't want it.
530b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         */
53191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
53291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        if (mIncomingScoThread == null) {
53391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            mIncomingScoThread = new IncomingScoAcceptThread();
53491b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            mIncomingScoThread.setName("incomingScoAcceptThread");
53591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            mIncomingScoThread.start();
536b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
537b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
538b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
539b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ synchronized void onBluetoothDisabled() {
54091b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        // Close off the SCO sockets
541a1478a9074b126d13124c99b7543b2518b3de3b7Eric Laurent        audioOff();
54291b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
54391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        if (mIncomingScoThread != null) {
544e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            mIncomingScoThread.shutdown();
545e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            mIncomingScoThread = null;
546b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
547b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
548b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
549b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean isHeadsetConnected() {
55090eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        if (mHeadset == null || mHeadsetType == TYPE_UNKNOWN) {
551b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return false;
552b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
553b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return mHeadset.isConnected();
554b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
555b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
556f6adf1a33b12c9900b3ce9c15130642ce515ae91Jaikumar Ganesh    /* package */ synchronized void connectHeadset(HeadsetBase headset, int headsetType) {
557b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mHeadset = headset;
558b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mHeadsetType = headsetType;
559b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (mHeadsetType == TYPE_HEADSET) {
560b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            initializeHeadsetAtParser();
561b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        } else {
562b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            initializeHandsfreeAtParser();
563b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
5641498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
5651498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        // Headset vendor-specific commands
5661498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        registerAllVendorSpecificCommands();
5671498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
568b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        headset.startEventThread();
569b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        configAudioParameters();
570b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
571b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (inDebug()) {
572b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            startDebug();
573b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
574b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
575b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (isIncallAudio()) {
576b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            audioOn();
57734a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi        } else if ( mCM.getFirstActiveRingingCall().isRinging()) {
57834a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi            // need to update HS with RING when single ringing call exist
57934a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi            mBluetoothPhoneState.ring();
580b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
581b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
582b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
583b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* returns true if there is some kind of in-call audio we may wish to route
584b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * bluetooth to */
585b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean isIncallAudio() {
5868058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        Call.State state = mCM.getActiveFgCallState();
587b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
588b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return (state == Call.State.ACTIVE || state == Call.State.ALERTING);
589b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
590b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
591819fc69ac472a8d77be8e5dd79a21434c6de8a37Jaikumar Ganesh    /* package */ synchronized void disconnectHeadset() {
592819fc69ac472a8d77be8e5dd79a21434c6de8a37Jaikumar Ganesh        audioOff();
5939760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie
5949760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie        // No need to check if isVirtualCallInProgress()
5959760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie        // terminateScoUsingVirtualVoiceCall() does the check
5969760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie        terminateScoUsingVirtualVoiceCall();
5979760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie
59890eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        mHeadsetType = TYPE_UNKNOWN;
599b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        stopDebug();
600b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        resetAtState();
601b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
602b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
603bfc44512504ebc93c101ddb394719840f2d25072Jaikumar Ganesh    /* package */ synchronized void resetAtState() {
604b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mClip = false;
605b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mIndicatorsEnabled = false;
6060966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly        mServiceConnectionEstablished = false;
607b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mCmee = false;
608c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        mClccTimestamps = new long[GSM_MAX_CONNECTIONS];
609c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        mClccUsed = new boolean[GSM_MAX_CONNECTIONS];
610c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        for (int i = 0; i < GSM_MAX_CONNECTIONS; i++) {
611b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mClccUsed[i] = false;
612b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
613b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mRemoteBrsf = 0;
614f5945125c4e044b860f89a370fe376b97bd91d37Jaikumar Ganesh        mPhonebook.resetAtState();
615b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
616b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
6177d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie    /* package */ HeadsetBase getHeadset() {
6187d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie        return mHeadset;
6197d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie    }
6207d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie
621b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private void configAudioParameters() {
622db065e9834bcad9b0cb16c272b4b272dee8bdf62Nick Pelly        String name = mHeadset.getRemoteDevice().getName();
623b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (name == null) {
624b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            name = "<unknown>";
625b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
626aa23e1c3c758bad23d8b6709147cc1ff7cd1e43cEric Laurent        mAudioManager.setParameters(HEADSET_NAME+"="+name+";"+HEADSET_NREC+"=on");
627b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
628b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
629b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
630b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** Represents the data that we send in a +CIND or +CIEV command to the HF
631b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
632b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private class BluetoothPhoneState {
633b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 0: no service
634b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 1: service
635b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mService;
636b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
637b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 0: no active call
638b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 1: active call (where active means audio is routed - not held call)
639b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mCall;
640b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
641b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 0: not in call setup
642b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 1: incoming call setup
643b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 2: outgoing call setup
644b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 3: remote party being alerted in an outgoing call setup
645b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mCallsetup;
646b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
647b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 0: no calls held
648b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 1: held call and active call
649b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 2: held call only
650b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mCallheld;
651b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
652b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // cellular signal strength of AG: 0-5
653b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mSignal;
654b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
655b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // cellular signal strength in CSQ rssi scale
656b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mRssi;  // for CSQ
657b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
658b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 0: roaming not active (home)
659b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 1: roaming active
660b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mRoam;
661b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
662b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // battery charge of AG: 0-5
663b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mBattchg;
664b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
665b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 0: not registered
666b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 1: registered, home network
667b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // 5: registered, roaming
668b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int mStat;  // for CREG
669b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
670b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private String mRingingNumber;  // Context for in-progress RING's
671b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int    mRingingType;
672b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private boolean mIgnoreRing = false;
6731ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh        private boolean mStopRing = false;
674b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
67590eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        // current or last call start timestamp
67690eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        private long mCallStartTime = 0;
67790eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        // time window to reconnect remotely-disconnected SCO
67890eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        // in mili-seconds
67990eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie        private static final int RETRY_SCO_TIME_WINDOW = 1000;
68090eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie
681b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private static final int SERVICE_STATE_CHANGED = 1;
682487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh        private static final int PRECISE_CALL_STATE_CHANGED = 2;
683b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private static final int RING = 3;
684a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman        private static final int PHONE_CDMA_CALL_WAITING = 4;
685b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
686b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private Handler mStateChangeHandler = new Handler() {
687b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
688b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public void handleMessage(Message msg) {
689b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                switch(msg.what) {
690b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                case RING:
691b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    AtCommandResult result = ring();
692b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (result != null) {
693b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        sendURC(result.toString());
694b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
695b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    break;
696b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                case SERVICE_STATE_CHANGED:
697b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
698b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    updateServiceState(sendUpdate(), state);
699b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    break;
700487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                case PRECISE_CALL_STATE_CHANGED:
701a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                case PHONE_CDMA_CALL_WAITING:
702b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    Connection connection = null;
703b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (((AsyncResult) msg.obj).result instanceof Connection) {
704b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        connection = (Connection) ((AsyncResult) msg.obj).result;
705b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
706487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    handlePreciseCallStateChange(sendUpdate(), connection);
707b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    break;
708b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
709b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
710b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        };
711b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
712b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private BluetoothPhoneState() {
713b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // init members
7148058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            // TODO May consider to repalce the default phone's state and signal
7158058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            //      by CallManagter's state and signal
7168058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            updateServiceState(false, mCM.getDefaultPhone().getServiceState());
717487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            handlePreciseCallStateChange(false, null);
718b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mBattchg = 5;  // There is currently no API to get battery level
719b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                           // on demand, so set to 5 and wait for an update
7208058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            mSignal = asuToSignal(mCM.getDefaultPhone().getSignalStrength());
721b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
722b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // register for updates
723f8453f9e061b237d83d006e07009402fa7680583John Wang            // Use the service state of default phone as BT service state to
724f8453f9e061b237d83d006e07009402fa7680583John Wang            // avoid situation such as no cell or wifi connection but still
725f8453f9e061b237d83d006e07009402fa7680583John Wang            // reporting in service (since SipPhone always reports in service).
726f8453f9e061b237d83d006e07009402fa7680583John Wang            mCM.getDefaultPhone().registerForServiceStateChanged(mStateChangeHandler,
727b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                                                  SERVICE_STATE_CHANGED, null);
7288058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            mCM.registerForPreciseCallStateChanged(mStateChangeHandler,
729487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    PRECISE_CALL_STATE_CHANGED, null);
7308058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            mCM.registerForCallWaiting(mStateChangeHandler,
7318058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                PHONE_CDMA_CALL_WAITING, null);
7328058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang
733b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
734b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            filter.addAction(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
7351d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
7367d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie            filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
737b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mContext.registerReceiver(mStateReceiver, filter);
738b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
739b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
740a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville        private void updateBtPhoneStateAfterRadioTechnologyChange() {
7418eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan            if(VDBG) Log.d(TAG, "updateBtPhoneStateAfterRadioTechnologyChange...");
742a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville
743a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville            //Unregister all events from the old obsolete phone
744f8453f9e061b237d83d006e07009402fa7680583John Wang            mCM.getDefaultPhone().unregisterForServiceStateChanged(mStateChangeHandler);
7458058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            mCM.unregisterForPreciseCallStateChanged(mStateChangeHandler);
7468058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            mCM.unregisterForCallWaiting(mStateChangeHandler);
747a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville
748a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville            //Register all events new to the new active phone
749f8453f9e061b237d83d006e07009402fa7680583John Wang            mCM.getDefaultPhone().registerForServiceStateChanged(mStateChangeHandler,
75019dd8f9c63ba7471c76fc31847a8063d18a83b6dJaikumar Ganesh                                                  SERVICE_STATE_CHANGED, null);
7518058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            mCM.registerForPreciseCallStateChanged(mStateChangeHandler,
752487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    PRECISE_CALL_STATE_CHANGED, null);
7538058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            mCM.registerForCallWaiting(mStateChangeHandler,
7548058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                PHONE_CDMA_CALL_WAITING, null);
755a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville        }
756a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville
757b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private boolean sendUpdate() {
75802369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS            return isHeadsetConnected() && mHeadsetType == TYPE_HANDSFREE && mIndicatorsEnabled
75902369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS                   && mServiceConnectionEstablished;
760b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
761b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
762b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private boolean sendClipUpdate() {
76302369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS            return isHeadsetConnected() && mHeadsetType == TYPE_HANDSFREE && mClip &&
76402369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS                   mServiceConnectionEstablished;
76502369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS        }
76602369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS
76702369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS        private boolean sendRingUpdate() {
76802369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS            if (isHeadsetConnected() && !mIgnoreRing && !mStopRing &&
76902369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS                    mCM.getFirstActiveRingingCall().isRinging()) {
77002369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS                if (mHeadsetType == TYPE_HANDSFREE) {
77102369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS                    return mServiceConnectionEstablished ? true : false;
77202369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS                }
77302369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS                return true;
77402369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS            }
77502369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS            return false;
776b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
777b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
7781ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh        private void stopRing() {
7791ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh            mStopRing = true;
7801ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh        }
7811ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh
782b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /* convert [0,31] ASU signal strength to the [0,5] expected by
783b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * bluetooth devices. Scale is similar to status bar policy
784b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         */
78534b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh        private int gsmAsuToSignal(SignalStrength signalStrength) {
78634b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            int asu = signalStrength.getGsmSignalStrength();
787b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if      (asu >= 16) return 5;
788b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            else if (asu >= 8)  return 4;
789b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            else if (asu >= 4)  return 3;
790b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            else if (asu >= 2)  return 2;
791b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            else if (asu >= 1)  return 1;
792b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            else                return 0;
793b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
794b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
79534b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh        /**
79634b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh         * Convert the cdma / evdo db levels to appropriate icon level.
79734b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh         * The scale is similar to the one used in status bar policy.
79834b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh         *
79934b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh         * @param signalStrength
80034b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh         * @return the icon level
801404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville         */
80234b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh        private int cdmaDbmEcioToSignal(SignalStrength signalStrength) {
80334b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            int levelDbm = 0;
80434b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            int levelEcio = 0;
80534b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            int cdmaIconLevel = 0;
80634b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            int evdoIconLevel = 0;
80734b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            int cdmaDbm = signalStrength.getCdmaDbm();
80834b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            int cdmaEcio = signalStrength.getCdmaEcio();
80934b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh
81034b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            if (cdmaDbm >= -75) levelDbm = 4;
81134b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            else if (cdmaDbm >= -85) levelDbm = 3;
81234b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            else if (cdmaDbm >= -95) levelDbm = 2;
81334b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            else if (cdmaDbm >= -100) levelDbm = 1;
81434b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            else levelDbm = 0;
81534b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh
81634b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            // Ec/Io are in dB*10
81734b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            if (cdmaEcio >= -90) levelEcio = 4;
81834b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            else if (cdmaEcio >= -110) levelEcio = 3;
81934b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            else if (cdmaEcio >= -130) levelEcio = 2;
82034b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            else if (cdmaEcio >= -150) levelEcio = 1;
82134b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            else levelEcio = 0;
82234b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh
82334b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
82434b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh
82513df4bba7e39d516317d005b4b917b1f1c6baf8dJaikumar Ganesh            if (mServiceState != null &&
826b0664bb07d16eef5a0dd70013ffd02ef6db3ebebRobert Greenwalt                  (mServiceState.getNetworkType() == TelephonyManager.NETWORK_TYPE_EVDO_0 ||
827b0664bb07d16eef5a0dd70013ffd02ef6db3ebebRobert Greenwalt                   mServiceState.getNetworkType() == TelephonyManager.NETWORK_TYPE_EVDO_A)) {
82834b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  int evdoEcio = signalStrength.getEvdoEcio();
82934b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  int evdoSnr = signalStrength.getEvdoSnr();
83034b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  int levelEvdoEcio = 0;
83134b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  int levelEvdoSnr = 0;
83234b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh
83334b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  // Ec/Io are in dB*10
83434b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  if (evdoEcio >= -650) levelEvdoEcio = 4;
83534b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  else if (evdoEcio >= -750) levelEvdoEcio = 3;
83634b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  else if (evdoEcio >= -900) levelEvdoEcio = 2;
83734b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  else if (evdoEcio >= -1050) levelEvdoEcio = 1;
83834b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  else levelEvdoEcio = 0;
83934b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh
84034b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  if (evdoSnr > 7) levelEvdoSnr = 4;
84134b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  else if (evdoSnr > 5) levelEvdoSnr = 3;
84234b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  else if (evdoSnr > 3) levelEvdoSnr = 2;
84334b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  else if (evdoSnr > 1) levelEvdoSnr = 1;
84434b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  else levelEvdoSnr = 0;
84534b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh
84634b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                  evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr;
84734b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            }
84834b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            // TODO(): There is a bug open regarding what should be sent.
84934b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            return (cdmaIconLevel > evdoIconLevel) ?  cdmaIconLevel : evdoIconLevel;
85034b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh
851404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville        }
852404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville
853404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville
854404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville        private int asuToSignal(SignalStrength signalStrength) {
85534b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh            if (signalStrength.isGsm()) {
85634b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                return gsmAsuToSignal(signalStrength);
857404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville            } else {
85834b9266fbd8c9c4d52e8faf708e02037a7ddcc4cJaikumar Ganesh                return cdmaDbmEcioToSignal(signalStrength);
859404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville            }
860404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville        }
861404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville
862404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville
863b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /* convert [0,5] signal strength to a rssi signal strength for CSQ
864b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * which is [0,31]. Despite the same scale, this is not the same value
865b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * as ASU.
866b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         */
867b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private int signalToRssi(int signal) {
868b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // using C4A suggested values
869b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            switch (signal) {
870b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case 0: return 0;
871b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case 1: return 4;
872b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case 2: return 8;
873b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case 3: return 13;
874b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case 4: return 19;
875b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case 5: return 31;
876b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
877b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return 0;
878b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
879b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
880b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
881b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private final BroadcastReceiver mStateReceiver = new BroadcastReceiver() {
882b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
883b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public void onReceive(Context context, Intent intent) {
884b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
885d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie                    Message msg = mHandler.obtainMessage(BATTERY_CHANGED, intent);
886d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie                    mHandler.sendMessage(msg);
887f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                } else if (intent.getAction().equals(
888f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                            TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED)) {
889d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie                    Message msg = mHandler.obtainMessage(SIGNAL_STRENGTH_CHANGED,
8906e2978b10d99879799e21ff246b2827721fe1260Matthew Xie                                                                    intent);
891d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie                    mHandler.sendMessage(msg);
8921d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                } else if (intent.getAction().equals(
8931d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
8941d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
8951d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                        BluetoothProfile.STATE_DISCONNECTED);
8961d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE,
8971d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                        BluetoothProfile.STATE_DISCONNECTED);
898f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                    BluetoothDevice device =
899f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
900105bb2145aba70c03b43bfab906c1b0551387309Jaikumar Ganesh
9011d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh
902105bb2145aba70c03b43bfab906c1b0551387309Jaikumar Ganesh                    // We are only concerned about Connected sinks to suspend and resume
903105bb2145aba70c03b43bfab906c1b0551387309Jaikumar Ganesh                    // them. We can safely ignore SINK_STATE_CHANGE for other devices.
904a43debb2022f4f4cbb7a2ca8aa3b2658b2da4231Eric Laurent                    if (device == null || (mA2dpDevice != null && !device.equals(mA2dpDevice))) {
905a43debb2022f4f4cbb7a2ca8aa3b2658b2da4231Eric Laurent                        return;
906a43debb2022f4f4cbb7a2ca8aa3b2658b2da4231Eric Laurent                    }
907105bb2145aba70c03b43bfab906c1b0551387309Jaikumar Ganesh
908f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                    synchronized (BluetoothHandsfree.this) {
909f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                        mA2dpState = state;
9101d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                        if (state == BluetoothProfile.STATE_DISCONNECTED) {
911105bb2145aba70c03b43bfab906c1b0551387309Jaikumar Ganesh                            mA2dpDevice = null;
912105bb2145aba70c03b43bfab906c1b0551387309Jaikumar Ganesh                        } else {
913105bb2145aba70c03b43bfab906c1b0551387309Jaikumar Ganesh                            mA2dpDevice = device;
914105bb2145aba70c03b43bfab906c1b0551387309Jaikumar Ganesh                        }
915ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent                        if (oldState == BluetoothA2dp.STATE_PLAYING &&
9161d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                            mA2dpState == BluetoothProfile.STATE_CONNECTED) {
917ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent                            if (mA2dpSuspended) {
918ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent                                if (mPendingSco) {
919ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent                                    mHandler.removeMessages(MESSAGE_CHECK_PENDING_SCO);
920ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent                                    if (DBG) log("A2DP suspended, completing SCO");
92191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                                    connectScoThread();
922ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent                                    mPendingSco = false;
923f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                                }
924f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                            }
925f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                        }
926f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                    }
9277d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                } else if (intent.getAction().
9287d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                           equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
9297d9521b93cadfde1e7fb2e7904641982f1a65bb6Matthew Xie                    mPhonebook.handleAccessPermissionResult(intent);
930b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
931b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
932b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        };
933b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
934b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private synchronized void updateBatteryState(Intent intent) {
935b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int batteryLevel = intent.getIntExtra("level", -1);
936b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int scale = intent.getIntExtra("scale", -1);
937b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (batteryLevel == -1 || scale == -1) {
938b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return;  // ignore
939b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
940b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            batteryLevel = batteryLevel * 5 / scale;
941b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (mBattchg != batteryLevel) {
942b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mBattchg = batteryLevel;
943b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (sendUpdate()) {
944b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    sendURC("+CIEV: 7," + mBattchg);
945b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
946b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
947b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
948b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
949b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private synchronized void updateSignalState(Intent intent) {
950404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville            // NOTE this function is called by the BroadcastReceiver mStateReceiver after intent
951404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville            // ACTION_SIGNAL_STRENGTH_CHANGED and by the DebugThread mDebugThread
95290eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie            if (!isHeadsetConnected()) {
953d16be9780f29c031e3d854fde5b92f05392836e3Jaikumar Ganesh                return;
954d16be9780f29c031e3d854fde5b92f05392836e3Jaikumar Ganesh            }
955d16be9780f29c031e3d854fde5b92f05392836e3Jaikumar Ganesh
956404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville            SignalStrength signalStrength = SignalStrength.newFromBundle(intent.getExtras());
957b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int signal;
958404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville
959404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville            if (signalStrength != null) {
960404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                signal = asuToSignal(signalStrength);
961404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                mRssi = signalToRssi(signal);  // no unsolicited CSQ
962404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                if (signal != mSignal) {
963404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                    mSignal = signal;
964404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                    if (sendUpdate()) {
965404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                        sendURC("+CIEV: 5," + mSignal);
966404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                    }
967b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
968404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville            } else {
969404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                Log.e(TAG, "Signal Strength null");
970b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
971b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
972b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
973b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private synchronized void updateServiceState(boolean sendUpdate, ServiceState state) {
974b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int service = state.getState() == ServiceState.STATE_IN_SERVICE ? 1 : 0;
975b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int roam = state.getRoaming() ? 1 : 0;
976b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int stat;
977b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            AtCommandResult result = new AtCommandResult(AtCommandResult.UNSOLICITED);
97813df4bba7e39d516317d005b4b917b1f1c6baf8dJaikumar Ganesh            mServiceState = state;
979b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (service == 0) {
980b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                stat = 0;
981b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            } else {
982b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                stat = (roam == 1) ? 5 : 1;
983b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
984b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
985b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (service != mService) {
986b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mService = service;
987b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (sendUpdate) {
988b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResponse("+CIEV: 1," + mService);
989b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
990b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
991b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (roam != mRoam) {
992b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mRoam = roam;
993b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (sendUpdate) {
994b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResponse("+CIEV: 6," + mRoam);
995b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
996b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
997b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (stat != mStat) {
998b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mStat = stat;
999b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (sendUpdate) {
1000b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResponse(toCregString());
1001b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1002b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1003b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1004b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sendURC(result.toString());
1005b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1006b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1007487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh        private synchronized void handlePreciseCallStateChange(boolean sendUpdate,
1008487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                Connection connection) {
1009b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int call = 0;
1010b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int callsetup = 0;
1011b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int callheld = 0;
1012b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int prevCallsetup = mCallsetup;
1013b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            AtCommandResult result = new AtCommandResult(AtCommandResult.UNSOLICITED);
10148058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            Call foregroundCall = mCM.getActiveFgCall();
10158058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            Call backgroundCall = mCM.getFirstActiveBgCall();
10168058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            Call ringingCall = mCM.getFirstActiveRingingCall();
1017b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
10188eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan            if (VDBG) log("updatePhoneState()");
1019b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1020487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            // This function will get called when the Precise Call State
1021487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            // {@link Call.State} changes. Hence, we might get this update
1022487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            // even if the {@link Phone.state} is same as before.
1023487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            // Check for the same.
1024487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh
10258058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            Phone.State newState = mCM.getState();
10261dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh            if (newState != mPhoneState) {
10271dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                mPhoneState = newState;
10281dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                switch (mPhoneState) {
10291dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                case IDLE:
10301dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                    mUserWantsAudio = true;  // out of call - reset state
10311dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                    audioOff();
10321dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                    break;
10331dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                default:
10341dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                    callStarted();
10351dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                }
1036b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1037b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
10388058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            switch(foregroundCall.getState()) {
1039b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case ACTIVE:
1040b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                call = 1;
1041b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mAudioPossible = true;
1042b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                break;
1043b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case DIALING:
1044b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                callsetup = 2;
1045a41427328932deb7cbadf586a1ec58e0ba26a932Jaikumar Ganesh                mAudioPossible = true;
10463904e979ce9a501acbe6d8739393767a495388e8Jaikumar Ganesh                // We also need to send a Call started indication
10473904e979ce9a501acbe6d8739393767a495388e8Jaikumar Ganesh                // for cases where the 2nd MO was initiated was
10483904e979ce9a501acbe6d8739393767a495388e8Jaikumar Ganesh                // from a *BT hands free* and is waiting for a
10493904e979ce9a501acbe6d8739393767a495388e8Jaikumar Ganesh                // +BLND: OK response
10503904e979ce9a501acbe6d8739393767a495388e8Jaikumar Ganesh                // There is a special case handling of the same case
10513904e979ce9a501acbe6d8739393767a495388e8Jaikumar Ganesh                // for CDMA below
10528058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                if (mCM.getFgPhone().getPhoneType() == Phone.PHONE_TYPE_GSM) {
10533904e979ce9a501acbe6d8739393767a495388e8Jaikumar Ganesh                    callStarted();
10543904e979ce9a501acbe6d8739393767a495388e8Jaikumar Ganesh                }
1055b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                break;
1056b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case ALERTING:
1057b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                callsetup = 3;
1058b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Open the SCO channel for the outgoing call.
105990eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie                mCallStartTime = System.currentTimeMillis();
1060b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                audioOn();
1061b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mAudioPossible = true;
1062b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                break;
106381a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh            case DISCONNECTING:
106481a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                // This is a transient state, we don't want to send
106581a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                // any AT commands during this state.
106681a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                call = mCall;
106781a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                callsetup = mCallsetup;
106881a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                callheld = mCallheld;
106981a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                break;
1070b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            default:
1071b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mAudioPossible = false;
1072b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1073b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
10748058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            switch(ringingCall.getState()) {
1075b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case INCOMING:
1076b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case WAITING:
1077b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                callsetup = 1;
1078b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                break;
107981a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh            case DISCONNECTING:
108081a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                // This is a transient state, we don't want to send
108181a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                // any AT commands during this state.
108281a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                call = mCall;
108381a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                callsetup = mCallsetup;
108481a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                callheld = mCallheld;
108581a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                break;
1086b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1087b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
10888058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            switch(backgroundCall.getState()) {
1089b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            case HOLDING:
1090b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (call == 1) {
1091b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    callheld = 1;
1092b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
1093b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    call = 1;
1094b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    callheld = 2;
1095b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1096b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                break;
109781a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh            case DISCONNECTING:
109881a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                // This is a transient state, we don't want to send
109981a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                // any AT commands during this state.
110081a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                call = mCall;
110181a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                callsetup = mCallsetup;
110281a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                callheld = mCallheld;
110381a26f368128024620b0dc7b0eb32debf3fe3419Jaikumar Ganesh                break;
1104b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1105b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1106b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (mCall != call) {
1107b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (call == 1) {
1108b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    // This means that a call has transitioned from NOT ACTIVE to ACTIVE.
1109b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    // Switch on audio.
111090eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie                    mCallStartTime = System.currentTimeMillis();
1111b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    audioOn();
1112b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1113b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mCall = call;
1114b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (sendUpdate) {
1115b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResponse("+CIEV: 2," + mCall);
1116b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1117b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1118b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (mCallsetup != callsetup) {
1119b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mCallsetup = callsetup;
1120b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (sendUpdate) {
11214b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    // If mCall = 0, send CIEV
11224b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    // mCall = 1, mCallsetup = 0, send CIEV
11234b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    // mCall = 1, mCallsetup = 1, send CIEV after CCWA,
11244b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    // if 3 way supported.
11254b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    // mCall = 1, mCallsetup = 2 / 3 -> send CIEV,
11264b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    // if 3 way is supported
11274b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                    if (mCall != 1 || mCallsetup == 0 ||
11284b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                        mCallsetup != 1 && (mRemoteBrsf & BRSF_HF_CW_THREE_WAY_CALLING) != 0x0) {
1129b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        result.addResponse("+CIEV: 3," + mCallsetup);
1130b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
1131b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1132b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1133b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
11348058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            if (mCM.getDefaultPhone().getPhoneType() == Phone.PHONE_TYPE_CDMA) {
1135c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                PhoneApp app = PhoneApp.getInstance();
113660877272381161201c6969dea501e683705d6e32w                if (app.cdmaPhoneCallState != null) {
1137487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    CdmaPhoneCallState.PhoneCallState currCdmaThreeWayCallState =
113860877272381161201c6969dea501e683705d6e32w                            app.cdmaPhoneCallState.getCurrentCallState();
1139487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    CdmaPhoneCallState.PhoneCallState prevCdmaThreeWayCallState =
1140ed1d155825eb32990fde95eef9d89a7260e4c3f1w                        app.cdmaPhoneCallState.getPreviousCallState();
1141ed1d155825eb32990fde95eef9d89a7260e4c3f1w
114291f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                    log("CDMA call state: " + currCdmaThreeWayCallState + " prev state:" +
114391f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                        prevCdmaThreeWayCallState);
1144487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    callheld = getCdmaCallHeldStatus(currCdmaThreeWayCallState,
1145487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                                     prevCdmaThreeWayCallState);
1146487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh
1147487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    if (mCdmaThreeWayCallState != currCdmaThreeWayCallState) {
1148487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // In CDMA, the network does not provide any feedback
1149487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // to the phone when the 2nd MO call goes through the
1150487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // stages of DIALING > ALERTING -> ACTIVE we fake the
1151487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // sequence
1152487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        if ((currCdmaThreeWayCallState ==
1153487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE)
1154487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                    && app.cdmaPhoneCallState.IsThreeWayCallOrigStateDialing()) {
1155487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            mAudioPossible = true;
1156487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            if (sendUpdate) {
1157487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                if ((mRemoteBrsf & BRSF_HF_CW_THREE_WAY_CALLING) != 0x0) {
1158487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                    result.addResponse("+CIEV: 3,2");
115991f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                    // Mimic putting the call on hold
116091f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                    result.addResponse("+CIEV: 4,1");
116191f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                    mCallheld = callheld;
1162487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                    result.addResponse("+CIEV: 3,3");
1163487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                    result.addResponse("+CIEV: 3,0");
1164487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                }
116560877272381161201c6969dea501e683705d6e32w                            }
1166487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            // We also need to send a Call started indication
1167487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            // for cases where the 2nd MO was initiated was
1168487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            // from a *BT hands free* and is waiting for a
1169487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            // +BLND: OK response
1170487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            callStarted();
1171c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                        }
1172c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1173487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // In CDMA, the network does not provide any feedback to
1174487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // the phone when a user merges a 3way call or swaps
1175487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // between two calls we need to send a CIEV response
1176487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // indicating that a call state got changed which should
1177487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // trigger a CLCC update request from the BT client.
1178487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        if (currCdmaThreeWayCallState ==
117991f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                CdmaPhoneCallState.PhoneCallState.CONF_CALL &&
118091f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                prevCdmaThreeWayCallState ==
118191f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                  CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
1182487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            mAudioPossible = true;
1183487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            if (sendUpdate) {
1184487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                if ((mRemoteBrsf & BRSF_HF_CW_THREE_WAY_CALLING) != 0x0) {
1185487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                    result.addResponse("+CIEV: 2,1");
1186487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                    result.addResponse("+CIEV: 3,0");
1187487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                }
118860877272381161201c6969dea501e683705d6e32w                            }
1189c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                        }
1190c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    }
1191487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    mCdmaThreeWayCallState = currCdmaThreeWayCallState;
1192c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                }
1193c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            }
1194c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
119591f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh            boolean callsSwitched;
119691f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh
119791f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh            if (mCM.getDefaultPhone().getPhoneType() == Phone.PHONE_TYPE_CDMA &&
119891f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                mCdmaThreeWayCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
119991f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                callsSwitched = mCdmaCallsSwapped;
120091f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh            } else {
120191f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                callsSwitched =
120291f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                    (callheld == 1 && ! (backgroundCall.getEarliestConnectTime() ==
120391f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                        mBgndEarliestConnectionTime));
120491f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                mBgndEarliestConnectionTime = backgroundCall.getEarliestConnectTime();
120591f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh            }
1206b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1207b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1208b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (mCallheld != callheld || callsSwitched) {
1209b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mCallheld = callheld;
1210b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (sendUpdate) {
1211b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResponse("+CIEV: 4," + mCallheld);
1212b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1213b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1214b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1215b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (callsetup == 1 && callsetup != prevCallsetup) {
1216b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // new incoming call
1217b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                String number = null;
1218b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                int type = 128;
1219b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // find incoming phone number and type
1220b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (connection == null) {
12218058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                    connection = ringingCall.getEarliestConnection();
1222b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (connection == null) {
1223b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        Log.e(TAG, "Could not get a handle on Connection object for new " +
1224b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                              "incoming call");
1225b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
1226b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1227b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (connection != null) {
1228b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    number = connection.getAddress();
1229b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (number != null) {
1230b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        type = PhoneNumberUtils.toaFromString(number);
1231b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
1232b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1233b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (number == null) {
1234b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    number = "";
1235b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1236b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if ((call != 0 || callheld != 0) && sendUpdate) {
1237b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    // call waiting
1238b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if ((mRemoteBrsf & BRSF_HF_CW_THREE_WAY_CALLING) != 0x0) {
1239b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        result.addResponse("+CCWA: \"" + number + "\"," + type);
12404b8337277ec2e375c3536b97c40e6617a7b12990The Android Open Source Project                        result.addResponse("+CIEV: 3," + callsetup);
1241b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
1242b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
1243b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    // regular new incoming call
1244b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mRingingNumber = number;
1245b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mRingingType = type;
1246b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mIgnoreRing = false;
12471ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh                    mStopRing = false;
1248b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1249a23972e3605fb235897250fd4edc2b70be13e00dNick Pelly                    if ((mLocalBrsf & BRSF_AG_IN_BAND_RING) != 0x0) {
125090eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie                        mCallStartTime = System.currentTimeMillis();
1251a23972e3605fb235897250fd4edc2b70be13e00dNick Pelly                        audioOn();
1252a23972e3605fb235897250fd4edc2b70be13e00dNick Pelly                    }
1253b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResult(ring());
1254b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1255b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1256b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sendURC(result.toString());
1257b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1258b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1259487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh        private int getCdmaCallHeldStatus(CdmaPhoneCallState.PhoneCallState currState,
1260487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                                  CdmaPhoneCallState.PhoneCallState prevState) {
1261487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            int callheld;
1262487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            // Update the Call held information
1263487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            if (currState == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
1264487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                if (prevState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
1265487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    callheld = 0; //0: no calls held, as now *both* the caller are active
1266487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                } else {
1267487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                    callheld = 1; //1: held call and active call, as on answering a
1268487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                            // Call Waiting, one of the caller *is* put on hold
1269487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                }
1270487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            } else if (currState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
1271487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                callheld = 1; //1: held call and active call, as on make a 3 Way Call
1272487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                        // the first caller *is* put on hold
1273487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            } else {
1274487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh                callheld = 0; //0: no calls held as this is a SINGLE_ACTIVE call
1275487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            }
1276487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh            return callheld;
1277487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh        }
1278487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh
1279487f0e7aa6b2db7212e76713199d4812a61c4625Jaikumar Ganesh
1280b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private AtCommandResult ring() {
128102369485c4c2b977e6a26cd01ab817b364758bfeSRINIVAS            if (sendRingUpdate()) {
1282b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                AtCommandResult result = new AtCommandResult(AtCommandResult.UNSOLICITED);
1283b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                result.addResponse("RING");
1284b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (sendClipUpdate()) {
1285b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResponse("+CLIP: \"" + mRingingNumber + "\"," + mRingingType);
1286b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1287b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1288b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                Message msg = mStateChangeHandler.obtainMessage(RING);
1289b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mStateChangeHandler.sendMessageDelayed(msg, 3000);
1290b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return result;
1291b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1292b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return null;
1293b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1294b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1295b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private synchronized String toCregString() {
1296b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return new String("+CREG: 1," + mStat);
1297b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1298b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
129991f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh        private synchronized void updateCallHeld() {
130091f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh            if (mCallheld != 0) {
130191f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                mCallheld = 0;
130291f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                sendURC("+CIEV: 4,0");
130391f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh            }
130491f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh        }
130591f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh
1306b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private synchronized AtCommandResult toCindResult() {
1307b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            AtCommandResult result = new AtCommandResult(AtCommandResult.OK);
1308b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            int call, call_setup;
1309b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
1310b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            // Handsfree carkits expect that +CIND is properly responded to.
1311b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            // Hence we ensure that a proper response is sent for the virtual call too.
1312b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            if (isVirtualCallInProgress()) {
1313b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                call = 1;
1314b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                call_setup = 0;
1315b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            } else {
1316b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                // regular phone call
1317b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                call = mCall;
1318b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                call_setup = mCallsetup;
1319b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            }
1320d16be9780f29c031e3d854fde5b92f05392836e3Jaikumar Ganesh
1321b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            mSignal = asuToSignal(mCM.getDefaultPhone().getSignalStrength());
1322b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            String status = "+CIND: " + mService + "," + call + "," + call_setup + "," +
1323b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            mCallheld + "," + mSignal + "," + mRoam + "," + mBattchg;
1324b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            result.addResponse(status);
1325b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return result;
1326b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1327b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1328b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private synchronized AtCommandResult toCsqResult() {
1329b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            AtCommandResult result = new AtCommandResult(AtCommandResult.OK);
1330b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            String status = "+CSQ: " + mRssi + ",99";
1331b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            result.addResponse(status);
1332b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return result;
1333b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1334b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1335b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1336b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private synchronized AtCommandResult getCindTestResult() {
1337b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return new AtCommandResult("+CIND: (\"service\",(0-1))," + "(\"call\",(0-1))," +
1338b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        "(\"callsetup\",(0-3)),(\"callheld\",(0-2)),(\"signal\",(0-5))," +
1339b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        "(\"roam\",(0-1)),(\"battchg\",(0-5))");
1340b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1341b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1342b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        private synchronized void ignoreRing() {
1343b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mCallsetup = 0;
1344b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mIgnoreRing = true;
1345b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (sendUpdate()) {
1346b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                sendURC("+CIEV: 3," + mCallsetup);
1347b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1348b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1349b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
135093db56932abb4ad52c446947048e2af69e5a9848Matthew Xie        private void scoClosed() {
135193db56932abb4ad52c446947048e2af69e5a9848Matthew Xie            // sync on mUserWantsAudio change
135293db56932abb4ad52c446947048e2af69e5a9848Matthew Xie            synchronized(BluetoothHandsfree.this) {
135390eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie                if (mUserWantsAudio &&
135490eae4375aa85e6ed620e32b12f655ef99356a70Matthew Xie                    System.currentTimeMillis() - mCallStartTime < RETRY_SCO_TIME_WINDOW) {
135593db56932abb4ad52c446947048e2af69e5a9848Matthew Xie                    Message msg = mHandler.obtainMessage(SCO_CONNECTION_CHECK);
135693db56932abb4ad52c446947048e2af69e5a9848Matthew Xie                    mHandler.sendMessage(msg);
135793db56932abb4ad52c446947048e2af69e5a9848Matthew Xie                }
135893db56932abb4ad52c446947048e2af69e5a9848Matthew Xie            }
135993db56932abb4ad52c446947048e2af69e5a9848Matthew Xie        }
1360b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    };
1361b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1362b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final int SCO_CLOSED = 3;
1363b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final int CHECK_CALL_STARTED = 4;
1364b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final int CHECK_VOICE_RECOGNITION_STARTED = 5;
1365f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan    private static final int MESSAGE_CHECK_PENDING_SCO = 6;
1366912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh    private static final int SCO_AUDIO_STATE = 7;
136793db56932abb4ad52c446947048e2af69e5a9848Matthew Xie    private static final int SCO_CONNECTION_CHECK = 8;
1368d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie    private static final int BATTERY_CHANGED = 9;
1369d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie    private static final int SIGNAL_STRENGTH_CHANGED = 10;
1370d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie
1371d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie    private final class HandsfreeMessageHandler extends Handler {
1372d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie        private HandsfreeMessageHandler(Looper looper) {
1373d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie            super(looper);
1374d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie        }
1375b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1376b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        @Override
13777757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh        public void handleMessage(Message msg) {
137887ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            switch (msg.what) {
137987ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            case SCO_CLOSED:
138087ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                synchronized (BluetoothHandsfree.this) {
138187ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // synchronized
138287ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // Make atomic against audioOn, userWantsAudioOn
138387ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // TODO finer lock to decouple from other call flow such as
138487ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    //      mWaitingForCallStart change
138587ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie
138691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                    audioOff();
138793db56932abb4ad52c446947048e2af69e5a9848Matthew Xie                    // notify mBluetoothPhoneState that the SCO channel has closed
138893db56932abb4ad52c446947048e2af69e5a9848Matthew Xie                    mBluetoothPhoneState.scoClosed();
138987ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                }
139087ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                break;
139187ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            case CHECK_CALL_STARTED:
139287ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                synchronized (BluetoothHandsfree.this) {
139387ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // synchronized
139487ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // Protect test/change of mWaitingForCallStart
13957757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                    if (mWaitingForCallStart) {
13967757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                        mWaitingForCallStart = false;
13977757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                        Log.e(TAG, "Timeout waiting for call to start");
13987757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                        sendURC("ERROR");
13997757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                        if (mStartCallWakeLock.isHeld()) {
14007757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                            mStartCallWakeLock.release();
14017757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                        }
14027757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                    }
140387ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                }
140487ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                break;
140587ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            case CHECK_VOICE_RECOGNITION_STARTED:
140687ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                synchronized (BluetoothHandsfree.this) {
140787ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // synchronized
140887ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // Protect test/change of mWaitingForVoiceRecognition
14097757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                    if (mWaitingForVoiceRecognition) {
14107757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                        mWaitingForVoiceRecognition = false;
14117757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                        Log.e(TAG, "Timeout waiting for voice recognition to start");
14127757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                        sendURC("ERROR");
14137757ebca8f27cd580bc85b6afe66ca43122b8c39Jaikumar Ganesh                    }
141487ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                }
141587ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                break;
141687ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            case MESSAGE_CHECK_PENDING_SCO:
141787ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                synchronized (BluetoothHandsfree.this) {
141887ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // synchronized
141987ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // Protect test/change of mPendingSco
1420f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                    if (mPendingSco && isA2dpMultiProfile()) {
1421f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                        Log.w(TAG, "Timeout suspending A2DP for SCO (mA2dpState = " +
1422f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                                mA2dpState + "). Starting SCO anyway");
142391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                        connectScoThread();
1424f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                        mPendingSco = false;
1425f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                    }
142687ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                }
142787ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                break;
142887ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            case SCO_AUDIO_STATE:
142987ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                BluetoothDevice device = (BluetoothDevice) msg.obj;
143087ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                if (getAudioState(device) == BluetoothHeadset.STATE_AUDIO_CONNECTING) {
143187ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    setAudioState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, device);
143287ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                }
143387ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                break;
143487ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            case SCO_CONNECTION_CHECK:
143587ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                synchronized (mBluetoothPhoneState) {
143687ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    // synchronized on mCall change
143787ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                    if (mBluetoothPhoneState.mCall == 1) {
143887ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                        // Sometimes, the SCO channel is torn down by HF with no reason.
143987ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                        // Because we are still in active call, reconnect SCO.
144087ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                        // audioOn does nothing if the SCO is already on.
144187ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                        audioOn();
144293db56932abb4ad52c446947048e2af69e5a9848Matthew Xie                    }
1443b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
144487ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                break;
144587ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            case BATTERY_CHANGED:
144687ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                mBluetoothPhoneState.updateBatteryState((Intent) msg.obj);
144787ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                break;
144887ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie            case SIGNAL_STRENGTH_CHANGED:
144987ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                mBluetoothPhoneState.updateSignalState((Intent) msg.obj);
145087ad6e7a229370ef0437f407b2e2a6d986025763Matthew Xie                break;
1451b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1452b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1453d9436d8b89bdb18e6ce5e7abcefeab60015d5e81Matthew Xie    }
14541d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh
14551d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private synchronized void setAudioState(int state, BluetoothDevice device) {
14561d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        if (VDBG) log("setAudioState(" + state + ")");
14571d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        if (mBluetoothHeadset == null) {
14581d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET);
14591d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            mPendingAudioState = true;
14601d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            mAudioState = state;
14611d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            return;
14621d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        }
14631d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        mBluetoothHeadset.setAudioState(device, state);
14644079f559beb3e414036056b4b33ad40de5e89c4aThe Android Open Source Project    }
14654079f559beb3e414036056b4b33ad40de5e89c4aThe Android Open Source Project
1466912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh    private synchronized int getAudioState(BluetoothDevice device) {
1467912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        if (mBluetoothHeadset == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1468912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh        return mBluetoothHeadset.getAudioState(device);
1469912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh    }
1470912186b04d22881fd025a7cbe586228b85721a05Jaikumar Ganesh
14711d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    private BluetoothProfile.ServiceListener mProfileListener =
14721d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            new BluetoothProfile.ServiceListener() {
14731d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public void onServiceConnected(int profile, BluetoothProfile proxy) {
14741d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            if (profile == BluetoothProfile.HEADSET) {
14751d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                mBluetoothHeadset = (BluetoothHeadset) proxy;
14761d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                synchronized(BluetoothHandsfree.this) {
14771d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    if (mPendingAudioState) {
14781d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                        mBluetoothHeadset.setAudioState(mHeadset.getRemoteDevice(), mAudioState);
14791d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                        mPendingAudioState = false;
14801d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                    }
14811d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                }
14821d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            } else if (profile == BluetoothProfile.A2DP) {
14831d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                mA2dp = (BluetoothA2dp) proxy;
14841d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            }
14851d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        }
14861d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        public void onServiceDisconnected(int profile) {
14871d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            if (profile == BluetoothProfile.HEADSET) {
14881d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                mBluetoothHeadset = null;
14891d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            } else if (profile == BluetoothProfile.A2DP) {
14901d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh                mA2dp = null;
14911d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh            }
14921d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh        }
14931d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh    };
14941d417d7ede71bbcf72a7edebf528efbfcf9b82bbJaikumar Ganesh
14951498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    /*
14961498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
14971498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     */
14981498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    private void broadcastVendorSpecificEventIntent(String command,
14991498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                                    int companyId,
150086324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh                                                    int commandType,
15011498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                                    Object[] arguments,
15021498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                                    BluetoothDevice device) {
15031498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        if (VDBG) log("broadcastVendorSpecificEventIntent(" + command + ")");
15041498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        Intent intent =
15051498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
15061498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
150786324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,
150886324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh                        commandType);
15091498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        // assert: all elements of args are Serializable
15101498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
15111498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
151286324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh
151386324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY
151486324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh            + "." + Integer.toString(companyId));
151586324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh
15161498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        mContext.sendBroadcast(intent, android.Manifest.permission.BLUETOOTH);
15171498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    }
15181498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
1519a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville    void updateBtHandsfreeAfterRadioTechnologyChange() {
15201498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        if (VDBG) Log.d(TAG, "updateBtHandsfreeAfterRadioTechnologyChange...");
1521a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville
15221dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh        mBluetoothPhoneState.updateBtPhoneStateAfterRadioTechnologyChange();
1523a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville    }
1524a69de9f9a2e99fe08f0009bb218b95ed7985c575Wink Saville
1525b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** Request to establish SCO (audio) connection to bluetooth
1526b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * headset/handsfree, if one is connected. Does not block.
1527b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * Returns false if the user has requested audio off, or if there
1528b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * is some other immediate problem that will prevent BT audio.
1529b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
1530b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ synchronized boolean audioOn() {
1531b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (VDBG) log("audioOn()");
1532b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (!isHeadsetConnected()) {
1533b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (DBG) log("audioOn(): headset is not connected!");
1534b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return false;
1535b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
15360966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly        if (mHeadsetType == TYPE_HANDSFREE && !mServiceConnectionEstablished) {
15370966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly            if (DBG) log("audioOn(): service connection not yet established!");
15380966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly            return false;
15390966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly        }
1540b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1541b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (mConnectedSco != null) {
1542b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (DBG) log("audioOn(): audio is already connected");
1543b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return true;
1544b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1545b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1546b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (!mUserWantsAudio) {
1547b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (DBG) log("audioOn(): user requested no audio, ignoring");
1548b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return false;
1549b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1550b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1551f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan        if (mPendingSco) {
1552f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan            if (DBG) log("audioOn(): SCO already pending");
1553f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan            return true;
1554f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan        }
1555f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan
1556ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent        mA2dpSuspended = false;
1557ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent        mPendingSco = false;
1558f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan        if (isA2dpMultiProfile() && mA2dpState == BluetoothA2dp.STATE_PLAYING) {
1559f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan            if (DBG) log("suspending A2DP stream for SCO");
1560ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent            mA2dpSuspended = mA2dp.suspendSink(mA2dpDevice);
1561ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent            if (mA2dpSuspended) {
1562ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent                mPendingSco = true;
1563f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                Message msg = mHandler.obtainMessage(MESSAGE_CHECK_PENDING_SCO);
1564f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                mHandler.sendMessageDelayed(msg, 2000);
1565f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan            } else {
1566f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                Log.w(TAG, "Could not suspend A2DP stream for SCO, going ahead with SCO");
1567337c7eb41d344b39efcf740cdc579257152a1c58Nick Pelly            }
1568337c7eb41d344b39efcf740cdc579257152a1c58Nick Pelly        }
1569337c7eb41d344b39efcf740cdc579257152a1c58Nick Pelly
1570337c7eb41d344b39efcf740cdc579257152a1c58Nick Pelly        if (!mPendingSco) {
157191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang            connectScoThread();
1572b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1573b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1574b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return true;
1575b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1576b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1577b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** Used to indicate the user requested BT audio on.
1578b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     *  This will establish SCO (BT audio), even if the user requested it off
1579b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     *  previously on this call.
1580b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
1581b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ synchronized void userWantsAudioOn() {
1582b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mUserWantsAudio = true;
1583b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        audioOn();
1584b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1585b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** Used to indicate the user requested BT audio off.
1586b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     *  This will prevent us from establishing BT audio again during this call
1587b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     *  if audioOn() is called.
1588b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
1589b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ synchronized void userWantsAudioOff() {
1590b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mUserWantsAudio = false;
1591b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        audioOff();
1592b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1593b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1594b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** Request to disconnect SCO (audio) connection to bluetooth
1595b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * headset/handsfree, if one is connected. Does not block.
1596b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
1597b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ synchronized void audioOff() {
1598819fc69ac472a8d77be8e5dd79a21434c6de8a37Jaikumar Ganesh        if (VDBG) log("audioOff(): mPendingSco: " + mPendingSco +
1599819fc69ac472a8d77be8e5dd79a21434c6de8a37Jaikumar Ganesh                ", mConnectedSco: " + mConnectedSco +
1600819fc69ac472a8d77be8e5dd79a21434c6de8a37Jaikumar Ganesh                ", mA2dpState: " + mA2dpState +
160191b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                ", mA2dpSuspended: " + mA2dpSuspended);
1602ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent
1603ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent        if (mA2dpSuspended) {
1604a1478a9074b126d13124c99b7543b2518b3de3b7Eric Laurent            if (isA2dpMultiProfile()) {
160591b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                if (DBG) log("resuming A2DP stream after disconnecting SCO");
160691b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang                mA2dp.resumeSink(mA2dpDevice);
1607a1478a9074b126d13124c99b7543b2518b3de3b7Eric Laurent            }
1608ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent            mA2dpSuspended = false;
1609ab377a6e67c195c230db42e98d597bf4deba14bdEric Laurent        }
1610b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1611310e6fb854504637a4cefd123d8dd387660cf811Nick Pelly        mPendingSco = false;
1612310e6fb854504637a4cefd123d8dd387660cf811Nick Pelly
161391b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang        if (mSignalScoCloseThread != null) {
1614e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            mSignalScoCloseThread.shutdown();
1615e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent            mSignalScoCloseThread = null;
1616b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
161791b4c99be8dbdd8fca6b76a0e34e8a4b989b2b87Danica Chang
161843d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        // Sync with setting mConnectScoThread to null to assure the validity of
161943d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        // the condition
162043d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie        synchronized (ScoSocketConnectThread.class) {
162143d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            if (mConnectScoThread != null) {
162243d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                mConnectScoThread.shutdown();
162343d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie                resetConnectScoThread();
162443d59ddb900224b9f6e9df6e3cfda6174fb57507Matthew Xie            }
1625b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1626819fc69ac472a8d77be8e5dd79a21434c6de8a37Jaikumar Ganesh
1627e5d4f38445a097390b72c53b40f86e126c0cd75eEric Laurent        closeConnectedSco();    // Should be closed already, but just in case
1628b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1629b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1630b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ boolean isAudioOn() {
1631b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return (mConnectedSco != null);
1632b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1633b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1634f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan    private boolean isA2dpMultiProfile() {
1635f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan        return mA2dp != null && mHeadset != null && mA2dpDevice != null &&
1636f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan                mA2dpDevice.equals(mHeadset.getRemoteDevice());
1637f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan    }
1638f9f946bc1e3ba287f0fa8f47a662e42f20892526Zhu Lan
1639b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ void ignoreRing() {
16401dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh        mBluetoothPhoneState.ignoreRing();
1641b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1642b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1643eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh    private void sendURC(String urc) {
1644b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (isHeadsetConnected()) {
1645b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mHeadset.sendURC(urc);
1646b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1647b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1648b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1649b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** helper to redial last dialled number */
1650b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private AtCommandResult redial() {
1651b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        String number = mPhonebook.getLastDialledNumber();
1652b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (number == null) {
1653b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // spec seems to suggest sending ERROR if we dont have a
1654b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // number to redial
16558eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan            if (VDBG) log("Bluetooth redial requested (+BLDN), but no previous " +
1656b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                  "outgoing calls found. Ignoring");
1657b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return new AtCommandResult(AtCommandResult.ERROR);
1658b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1659b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        // Outgoing call initiated by the handsfree device
1660a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        // Send terminateScoUsingVirtualVoiceCall
1661a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        terminateScoUsingVirtualVoiceCall();
1662b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
166365454c803eb305c4740885ad4995a871b034a58aDavid Brown                Uri.fromParts(Constants.SCHEME_TEL, number, null));
1664b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1665b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mContext.startActivity(intent);
1666b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1667b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // We do not immediately respond OK, wait until we get a phone state
1668b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // update. If we return OK now and the handsfree immeidately requests
1669b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // our phone state it will say we are not in call yet which confuses
1670b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // some devices
1671b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        expectCallStart();
1672b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return new AtCommandResult(AtCommandResult.UNSOLICITED);  // send nothing
1673b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1674b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1675b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** Build the +CLCC result
1676b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     *  The complexity arises from the fact that we need to maintain the same
1677b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     *  CLCC index even as a call moves between states. */
1678c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    private synchronized AtCommandResult gsmGetClccResult() {
1679b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Collect all known connections
1680c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        Connection[] clccConnections = new Connection[GSM_MAX_CONNECTIONS];  // indexed by CLCC index
1681b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        LinkedList<Connection> newConnections = new LinkedList<Connection>();
1682b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        LinkedList<Connection> connections = new LinkedList<Connection>();
1683a50e10e2efadac960987eaadc0938c6f92d3ee90John Wang
16848058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        Call foregroundCall = mCM.getActiveFgCall();
16858058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        Call backgroundCall = mCM.getFirstActiveBgCall();
16868058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        Call ringingCall = mCM.getFirstActiveRingingCall();
16878058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang
16888058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        if (ringingCall.getState().isAlive()) {
16898058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            connections.addAll(ringingCall.getConnections());
1690b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
16918058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        if (foregroundCall.getState().isAlive()) {
16928058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            connections.addAll(foregroundCall.getConnections());
1693b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
16948058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        if (backgroundCall.getState().isAlive()) {
16958058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            connections.addAll(backgroundCall.getConnections());
1696b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1697b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1698b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Mark connections that we already known about
1699c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        boolean clccUsed[] = new boolean[GSM_MAX_CONNECTIONS];
1700c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        for (int i = 0; i < GSM_MAX_CONNECTIONS; i++) {
1701b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            clccUsed[i] = mClccUsed[i];
1702b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mClccUsed[i] = false;
1703b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1704b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        for (Connection c : connections) {
1705b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            boolean found = false;
1706b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            long timestamp = c.getCreateTime();
1707c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            for (int i = 0; i < GSM_MAX_CONNECTIONS; i++) {
1708b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (clccUsed[i] && timestamp == mClccTimestamps[i]) {
1709b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mClccUsed[i] = true;
1710b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    found = true;
1711b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    clccConnections[i] = c;
1712b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    break;
1713b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1714b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1715b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (!found) {
1716b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                newConnections.add(c);
1717b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1718b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1719b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1720b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Find a CLCC index for new connections
1721b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        while (!newConnections.isEmpty()) {
1722b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // Find lowest empty index
1723b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            int i = 0;
1724b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            while (mClccUsed[i]) i++;
1725b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // Find earliest connection
1726b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            long earliestTimestamp = newConnections.get(0).getCreateTime();
1727b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            Connection earliestConnection = newConnections.get(0);
1728b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            for (int j = 0; j < newConnections.size(); j++) {
1729b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                long timestamp = newConnections.get(j).getCreateTime();
1730b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (timestamp < earliestTimestamp) {
1731b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    earliestTimestamp = timestamp;
1732b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    earliestConnection = newConnections.get(j);
1733b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1734b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1735b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1736b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // update
1737b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mClccUsed[i] = true;
1738b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mClccTimestamps[i] = earliestTimestamp;
1739b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            clccConnections[i] = earliestConnection;
1740b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            newConnections.remove(earliestConnection);
1741b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1742b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1743b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Build CLCC
1744b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        AtCommandResult result = new AtCommandResult(AtCommandResult.OK);
1745b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        for (int i = 0; i < clccConnections.length; i++) {
1746b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (mClccUsed[i]) {
1747b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                String clccEntry = connectionToClccEntry(i, clccConnections[i]);
1748b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (clccEntry != null) {
1749b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResponse(clccEntry);
1750b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
1751b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
1752b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1753b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1754b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return result;
1755b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1756b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1757b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** Convert a Connection object into a single +CLCC result */
1758b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private String connectionToClccEntry(int index, Connection c) {
1759b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        int state;
1760b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        switch (c.getState()) {
1761b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case ACTIVE:
1762b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            state = 0;
1763b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            break;
1764b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case HOLDING:
1765b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            state = 1;
1766b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            break;
1767b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case DIALING:
1768b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            state = 2;
1769b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            break;
1770b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case ALERTING:
1771b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            state = 3;
1772b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            break;
1773b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case INCOMING:
1774b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            state = 4;
1775b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            break;
1776b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        case WAITING:
1777b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            state = 5;
1778b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            break;
1779b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        default:
1780b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return null;  // bad state
1781b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1782b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1783b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        int mpty = 0;
1784b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        Call call = c.getCall();
1785b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (call != null) {
1786b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mpty = call.isMultiparty() ? 1 : 0;
1787b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1788b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1789b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        int direction = c.isIncoming() ? 1 : 0;
1790b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1791b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        String number = c.getAddress();
1792b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        int type = -1;
1793b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (number != null) {
1794b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            type = PhoneNumberUtils.toaFromString(number);
1795b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1796b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
1797b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        String result = "+CLCC: " + (index + 1) + "," + direction + "," + state + ",0," + mpty;
1798b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (number != null) {
1799b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            result += ",\"" + number + "\"," + type;
1800b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
1801b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return result;
1802b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
1803c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1804c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    /** Build the +CLCC result for CDMA
1805c9d9ed30aa547b79b81adc13a4d148a003b6ee62w     *  The complexity arises from the fact that we need to maintain the same
1806c9d9ed30aa547b79b81adc13a4d148a003b6ee62w     *  CLCC index even as a call moves between states. */
1807c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    private synchronized AtCommandResult cdmaGetClccResult() {
1808c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        // In CDMA at one time a user can have only two live/active connections
1809c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        Connection[] clccConnections = new Connection[CDMA_MAX_CONNECTIONS];// indexed by CLCC index
18108058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        Call foregroundCall = mCM.getActiveFgCall();
18118058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        Call ringingCall = mCM.getFirstActiveRingingCall();
1812c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
18138058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        Call.State ringingCallState = ringingCall.getState();
1814c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        // If the Ringing Call state is INCOMING, that means this is the very first call
1815c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        // hence there should not be any Foreground Call
1816c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        if (ringingCallState == Call.State.INCOMING) {
18178eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan            if (VDBG) log("Filling clccConnections[0] for INCOMING state");
18188058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            clccConnections[0] = ringingCall.getLatestConnection();
18198058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        } else if (foregroundCall.getState().isAlive()) {
1820c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            // Getting Foreground Call connection based on Call state
18218058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang            if (ringingCall.isRinging()) {
18228eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan                if (VDBG) log("Filling clccConnections[0] & [1] for CALL WAITING state");
18238058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                clccConnections[0] = foregroundCall.getEarliestConnection();
18248058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                clccConnections[1] = ringingCall.getLatestConnection();
1825c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            } else {
18268058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                if (foregroundCall.getConnections().size() <= 1) {
1827c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    // Single call scenario
18288eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan                    if (VDBG) log("Filling clccConnections[0] with ForgroundCall latest connection");
18298058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                    clccConnections[0] = foregroundCall.getLatestConnection();
1830c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                } else {
1831c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    // Multiple Call scenario. This would be true for both
1832c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    // CONF_CALL and THRWAY_ACTIVE state
18338eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan                    if (VDBG) log("Filling clccConnections[0] & [1] with ForgroundCall connections");
18348058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                    clccConnections[0] = foregroundCall.getEarliestConnection();
18358058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                    clccConnections[1] = foregroundCall.getLatestConnection();
1836c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                }
1837c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            }
1838c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        }
1839c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1840c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        // Update the mCdmaIsSecondCallActive flag based on the Phone call state
1841c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        if (PhoneApp.getInstance().cdmaPhoneCallState.getCurrentCallState()
1842c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                == CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE) {
1843c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            cdmaSetSecondCallState(false);
1844c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        } else if (PhoneApp.getInstance().cdmaPhoneCallState.getCurrentCallState()
1845c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
1846c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            cdmaSetSecondCallState(true);
1847c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        }
1848c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1849c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        // Build CLCC
1850c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        AtCommandResult result = new AtCommandResult(AtCommandResult.OK);
1851c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        for (int i = 0; (i < clccConnections.length) && (clccConnections[i] != null); i++) {
1852c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            String clccEntry = cdmaConnectionToClccEntry(i, clccConnections[i]);
1853c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            if (clccEntry != null) {
1854c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                result.addResponse(clccEntry);
1855c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            }
1856c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        }
1857c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1858c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        return result;
1859c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    }
1860c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1861c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    /** Convert a Connection object into a single +CLCC result for CDMA phones */
1862c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    private String cdmaConnectionToClccEntry(int index, Connection c) {
1863c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        int state;
1864c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        PhoneApp app = PhoneApp.getInstance();
1865c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        CdmaPhoneCallState.PhoneCallState currCdmaCallState =
1866c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                app.cdmaPhoneCallState.getCurrentCallState();
1867c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        CdmaPhoneCallState.PhoneCallState prevCdmaCallState =
1868c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                app.cdmaPhoneCallState.getPreviousCallState();
1869c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1870c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        if ((prevCdmaCallState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE)
1871c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                && (currCdmaCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL)) {
1872c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            // If the current state is reached after merging two calls
1873c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            // we set the state of all the connections as ACTIVE
1874c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            state = 0;
1875c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        } else {
1876c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            switch (c.getState()) {
1877c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            case ACTIVE:
1878c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                // For CDMA since both the connections are set as active by FW after accepting
1879c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                // a Call waiting or making a 3 way call, we need to set the state specifically
1880c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                // to ACTIVE/HOLDING based on the mCdmaIsSecondCallActive flag. This way the
1881c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                // CLCC result will allow BT devices to enable the swap or merge options
1882c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                if (index == 0) { // For the 1st active connection
1883c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    state = mCdmaIsSecondCallActive ? 1 : 0;
1884c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                } else { // for the 2nd active connection
1885c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    state = mCdmaIsSecondCallActive ? 0 : 1;
1886c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                }
1887c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                break;
1888c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            case HOLDING:
1889c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                state = 1;
1890c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                break;
1891c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            case DIALING:
1892c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                state = 2;
1893c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                break;
1894c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            case ALERTING:
1895c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                state = 3;
1896c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                break;
1897c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            case INCOMING:
1898c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                state = 4;
1899c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                break;
1900c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            case WAITING:
1901c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                state = 5;
1902c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                break;
1903c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            default:
1904c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                return null;  // bad state
1905c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            }
1906c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        }
1907c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1908c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        int mpty = 0;
19093eb2a4ae98eb737004d40f7ce03a8d83883fb079Kuanting Zhu        if (currCdmaCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
1910c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi            if (prevCdmaCallState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
1911c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                // If the current state is reached after merging two calls
1912c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                // we set the multiparty call true.
1913c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                mpty = 1;
1914c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi            } else {
1915c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                // CALL_CONF state is not from merging two calls, but from
1916c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                // accepting the second call. In this case first will be on
1917c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                // hold in most cases but in some cases its already merged.
1918c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                // However, we will follow the common case and the test case
1919c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                // as per Bluetooth SIG PTS
1920c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi                mpty = 0;
1921c39c3c9ada26aa8fab1373ad94c9b0d9e8875caaSrinivas Krovvidi            }
19223eb2a4ae98eb737004d40f7ce03a8d83883fb079Kuanting Zhu        } else {
19233eb2a4ae98eb737004d40f7ce03a8d83883fb079Kuanting Zhu            mpty = 0;
1924c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        }
1925c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1926c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        int direction = c.isIncoming() ? 1 : 0;
1927c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1928c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        String number = c.getAddress();
1929c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        int type = -1;
1930c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        if (number != null) {
1931c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            type = PhoneNumberUtils.toaFromString(number);
1932c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        }
1933c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
1934c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        String result = "+CLCC: " + (index + 1) + "," + direction + "," + state + ",0," + mpty;
1935c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        if (number != null) {
1936c9d9ed30aa547b79b81adc13a4d148a003b6ee62w            result += ",\"" + number + "\"," + type;
1937c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        }
1938c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        return result;
1939c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    }
1940c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
19411498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    /*
19421498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * Register a vendor-specific command.
19431498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * @param commandName the name of the command.  For example, if the expected
19441498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * incoming command is <code>AT+FOO=bar,baz</code>, the value of this should be
19451498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * <code>"+FOO"</code>.
19461498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * @param companyId the Bluetooth SIG Company Identifier
19471498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * @param parser the AtParser on which to register the command
19481498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     */
19491498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    private void registerVendorSpecificCommand(String commandName,
19501498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                               int companyId,
19511498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                               AtParser parser) {
19521498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        parser.register(commandName,
19531498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                        new VendorSpecificCommandHandler(commandName, companyId));
19541498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    }
19551498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
19561498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    /*
19571498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * Register all vendor-specific commands here.
19581498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     */
19591498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    private void registerAllVendorSpecificCommands() {
19601498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        AtParser parser = mHeadset.getAtParser();
19611498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
19621498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        // Plantronics-specific headset events go here
19631498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        registerVendorSpecificCommand("+XEVENT",
19641498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                      BluetoothAssignedNumbers.PLANTRONICS,
19651498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                      parser);
19661498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    }
19671498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
1968b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /**
1969b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * Register AT Command handlers to implement the Headset profile
1970b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
1971b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private void initializeHeadsetAtParser() {
19728eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan        if (VDBG) log("Registering Headset AT commands");
1973b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        AtParser parser = mHeadset.getAtParser();
19741498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        // Headsets usually only have one button, which is meant to cause the
1975b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // HS to send us AT+CKPD=200 or AT+CKPD.
1976b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CKPD", new AtCommandHandler() {
1977b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            private AtCommandResult headsetButtonPress() {
1978eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                if (mCM.getFirstActiveRingingCall().isRinging()) {
1979eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    // Answer the call
1980eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    mBluetoothPhoneState.stopRing();
1981eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    sendURC("OK");
1982eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
1983eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    // If in-band ring tone is supported, SCO connection will already
1984eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    // be up and the following call will just return.
1985eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    audioOn();
19861ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh                    return new AtCommandResult(AtCommandResult.UNSOLICITED);
1987eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                } else if (mCM.hasActiveFgCall()) {
1988eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    if (!isAudioOn()) {
1989eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                        // Transfer audio from AG to HS
1990b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        audioOn();
1991eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    } else {
1992eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                        if (mHeadset.getDirection() == HeadsetBase.DIRECTION_INCOMING &&
1993eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                          (System.currentTimeMillis() - mHeadset.getConnectTimestamp()) < 5000) {
1994eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                            // Headset made a recent ACL connection to us - and
1995eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                            // made a mandatory AT+CKPD request to connect
1996eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                            // audio which races with our automatic audio
1997eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                            // setup.  ignore
1998b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        } else {
1999eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                            // Hang up the call
2000eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                            audioOff();
2001eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                            PhoneUtils.hangup(PhoneApp.getInstance().mCM);
2002b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        }
2003b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2004eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    return new AtCommandResult(AtCommandResult.OK);
2005eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                } else {
2006eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    // No current call - redial last number
2007eeab0c52a0542179621ee7854d7913cfd2445429Jaikumar Ganesh                    return redial();
2008b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2009b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2010b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2011b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2012b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return headsetButtonPress();
2013b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2014b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2015b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2016b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return headsetButtonPress();
2017b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2018b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2019b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2020b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2021b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /**
2022b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     * Register AT Command handlers to implement the Handsfree profile
2023b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     */
2024b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private void initializeHandsfreeAtParser() {
20258eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan        if (VDBG) log("Registering Handsfree AT commands");
2026b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        AtParser parser = mHeadset.getAtParser();
20278058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang        final Phone phone = mCM.getDefaultPhone();
2028b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2029b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Answer
2030b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register('A', new AtCommandHandler() {
2031b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2032b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleBasicCommand(String args) {
20331ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh                sendURC("OK");
20341ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh                mBluetoothPhoneState.stopRing();
20358058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
20361ec7e6b9b3bf1dfc384676244bec1edb50fedc58Jaikumar Ganesh                return new AtCommandResult(AtCommandResult.UNSOLICITED);
2037b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2038b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2039b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register('D', new AtCommandHandler() {
2040b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2041b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleBasicCommand(String args) {
2042b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length() > 0) {
2043b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (args.charAt(0) == '>') {
2044b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        // Yuck - memory dialling requested.
2045b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        // Just dial last number for now
2046b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        if (args.startsWith(">9999")) {   // for PTS test
2047b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            return new AtCommandResult(AtCommandResult.ERROR);
2048b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        }
2049b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        return redial();
2050b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    } else {
2051a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                        // Send terminateScoUsingVirtualVoiceCall
2052a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                        terminateScoUsingVirtualVoiceCall();
2053b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        // Remove trailing ';'
2054b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        if (args.charAt(args.length() - 1) == ';') {
2055b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            args = args.substring(0, args.length() - 1);
2056b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        }
2057eccbc2aa0b0a719bcadcdba5e1aeb22a5814d08ainshik
2058eccbc2aa0b0a719bcadcdba5e1aeb22a5814d08ainshik                        args = PhoneNumberUtils.convertPreDial(args);
2059eccbc2aa0b0a719bcadcdba5e1aeb22a5814d08ainshik
2060b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
206165454c803eb305c4740885ad4995a871b034a58aDavid Brown                                Uri.fromParts(Constants.SCHEME_TEL, args, null));
2062b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2063b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        mContext.startActivity(intent);
2064b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2065b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        expectCallStart();
2066b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        return new AtCommandResult(AtCommandResult.UNSOLICITED);  // send nothing
2067b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2068b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2069b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.ERROR);
2070b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2071b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2072b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2073b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Hang-up command
2074b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CHUP", new AtCommandHandler() {
2075b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2076b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2077586668fe6a1259083fbbc67de8ecac850c1475f1Jaikumar Ganesh                sendURC("OK");
2078b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                if (isVirtualCallInProgress()) {
2079a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh                    terminateScoUsingVirtualVoiceCall();
2080b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                } else {
2081b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    if (mCM.hasActiveFgCall()) {
2082b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                        PhoneUtils.hangupActiveCall(mCM.getActiveFgCall());
2083b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    } else if (mCM.hasActiveRingingCall()) {
2084b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                        PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall());
2085b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    } else if (mCM.hasActiveBgCall()) {
2086b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                        PhoneUtils.hangupHoldingCall(mCM.getFirstActiveBgCall());
2087b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    }
2088b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2089586668fe6a1259083fbbc67de8ecac850c1475f1Jaikumar Ganesh                return new AtCommandResult(AtCommandResult.UNSOLICITED);
2090b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2091b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2092b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2093b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Bluetooth Retrieve Supported Features command
2094b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+BRSF", new AtCommandHandler() {
2095b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            private AtCommandResult sendBRSF() {
2096b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+BRSF: " + mLocalBrsf);
2097b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2098b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2099b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2100b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // AT+BRSF=<handsfree supported features bitmap>
2101b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Handsfree is telling us which features it supports. We
2102b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // send the features we support
2103b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length == 1 && (args[0] instanceof Integer)) {
2104b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mRemoteBrsf = (Integer) args[0];
2105b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
2106b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    Log.w(TAG, "HF didn't sent BRSF assuming 0");
2107b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2108b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return sendBRSF();
2109b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2110b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2111b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2112b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // This seems to be out of spec, but lets do the nice thing
2113b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return sendBRSF();
2114b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2115b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2116b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
2117b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // This seems to be out of spec, but lets do the nice thing
2118b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return sendBRSF();
2119b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2120b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2121b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2122b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Call waiting notification on/off
2123b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CCWA", new AtCommandHandler() {
2124b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2125b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2126b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Seems to be out of spec, but lets return nicely
2127b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.OK);
2128b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2129b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2130b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
2131b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Call waiting is always on
2132b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CCWA: 1");
2133b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2134b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2135b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2136b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // AT+CCWA=<n>
2137b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Handsfree is trying to enable/disable call waiting. We
2138b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // cannot disable in the current implementation.
2139b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.OK);
2140b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2141b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2142b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleTestCommand() {
2143b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Request for range of supported CCWA paramters
2144b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CCWA: (\"n\",(1))");
2145b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2146b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2147b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2148b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Mobile Equipment Event Reporting enable/disable command
2149b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Of the full 3GPP syntax paramters (mode, keyp, disp, ind, bfr) we
2150b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // only support paramter ind (disable/enable evert reporting using
2151b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // +CDEV)
2152b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CMER", new AtCommandHandler() {
2153b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2154b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
2155b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(
2156b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        "+CMER: 3,0,0," + (mIndicatorsEnabled ? "1" : "0"));
2157b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2158b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2159b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2160b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length < 4) {
2161b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    // This is a syntax error
2162b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.ERROR);
2163b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else if (args[0].equals(3) && args[1].equals(0) &&
2164b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                           args[2].equals(0)) {
21650966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                    boolean valid = false;
2166b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (args[3].equals(0)) {
2167b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        mIndicatorsEnabled = false;
21680966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                        valid = true;
2169b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    } else if (args[3].equals(1)) {
2170b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        mIndicatorsEnabled = true;
21710966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                        valid = true;
21720966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                    }
21730966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                    if (valid) {
21740966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                        if ((mRemoteBrsf & BRSF_HF_CW_THREE_WAY_CALLING) == 0x0) {
21750966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                            mServiceConnectionEstablished = true;
21760966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                            sendURC("OK");  // send immediately, then initiate audio
21770966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                            if (isIncallAudio()) {
21780966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                                audioOn();
217934a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi                            } else if (mCM.getFirstActiveRingingCall().isRinging()) {
218034a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi                                // need to update HS with RING cmd when single
218134a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi                                // ringing call exist
218234a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi                                mBluetoothPhoneState.ring();
21830966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                            }
21840966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                            // only send OK once
21850966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                            return new AtCommandResult(AtCommandResult.UNSOLICITED);
21860966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                        } else {
21870966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                            return new AtCommandResult(AtCommandResult.OK);
21880966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                        }
2189b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2190b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
21910966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                return reportCmeError(BluetoothCmeError.OPERATION_NOT_SUPPORTED);
2192b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2193b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2194b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleTestCommand() {
2195b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CMER: (3),(0),(0),(0-1)");
2196b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2197b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2198b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2199b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Mobile Equipment Error Reporting enable/disable
2200b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CMEE", new AtCommandHandler() {
2201b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2202b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2203b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // out of spec, assume they want to enable
2204b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mCmee = true;
2205b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.OK);
2206b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2207b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2208b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
2209b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CMEE: " + (mCmee ? "1" : "0"));
2210b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2211b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2212b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2213b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // AT+CMEE=<n>
2214b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length == 0) {
2215b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    // <n> ommitted - default to 0
2216b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mCmee = false;
2217b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.OK);
2218b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else if (!(args[0] instanceof Integer)) {
2219b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    // Syntax error
2220b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.ERROR);
2221b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
2222b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mCmee = ((Integer)args[0] == 1);
2223b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.OK);
2224b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2225b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2226b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2227b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleTestCommand() {
2228b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Probably not required but spec, but no harm done
2229b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CMEE: (0-1)");
2230b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2231b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2232b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2233b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Bluetooth Last Dialled Number
2234b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+BLDN", new AtCommandHandler() {
2235b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2236b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2237b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return redial();
2238b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2239b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2240b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2241b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Indicator Update command
2242b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CIND", new AtCommandHandler() {
2243b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2244b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
22451dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                return mBluetoothPhoneState.toCindResult();
2246b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2247b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2248b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleTestCommand() {
22491dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                return mBluetoothPhoneState.getCindTestResult();
2250b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2251b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2252b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2253b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Query Signal Quality (legacy)
2254b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CSQ", new AtCommandHandler() {
2255b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2256b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
22571dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                return mBluetoothPhoneState.toCsqResult();
2258b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2259b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2260b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2261b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Query network registration state
2262b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CREG", new AtCommandHandler() {
2263b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2264b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
22651dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                return new AtCommandResult(mBluetoothPhoneState.toCregString());
2266b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2267b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2268b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2269b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Send DTMF. I don't know if we are also expected to play the DTMF tone
2270b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // locally, right now we don't
2271b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+VTS", new AtCommandHandler() {
2272b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2273b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2274b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length >= 1) {
2275b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    char c;
2276b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (args[0] instanceof Integer) {
2277b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        c = ((Integer) args[0]).toString().charAt(0);
2278b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    } else {
2279b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        c = ((String) args[0]).charAt(0);
2280b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2281b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (isValidDtmf(c)) {
22828058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                        phone.sendDtmf(c);
2283b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        return new AtCommandResult(AtCommandResult.OK);
2284b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2285b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2286b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.ERROR);
2287b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2288b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            private boolean isValidDtmf(char c) {
2289b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                switch (c) {
2290b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                case '#':
2291b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                case '*':
2292b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return true;
2293b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                default:
2294b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (Character.digit(c, 14) != -1) {
2295b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        return true;  // 0-9 and A-D
2296b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2297b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return false;
2298b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2299b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2300b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2301b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2302b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // List calls
2303b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CLCC", new AtCommandHandler() {
2304b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2305b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
23068058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                int phoneType = phone.getPhoneType();
2307b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                // Handsfree carkits expect that +CLCC is properly responded to.
2308b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                // Hence we ensure that a proper response is sent for the virtual call too.
2309b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                if (isVirtualCallInProgress()) {
2310b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    String number = phone.getLine1Number();
2311b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    AtCommandResult result = new AtCommandResult(AtCommandResult.OK);
2312b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    String args;
2313b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    if (number == null) {
2314b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                        args = "+CLCC: 1,0,0,0,0,\"\",0";
2315b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    }
2316b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    else
2317b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    {
2318b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                        args = "+CLCC: 1,0,0,0,0,\"" + number + "\"," +
2319b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                                  PhoneNumberUtils.toaFromString(number);
2320b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    }
2321b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    result.addResponse(args);
2322b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                    return result;
2323b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh                }
232479b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                if (phoneType == Phone.PHONE_TYPE_CDMA) {
2325c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    return cdmaGetClccResult();
232679b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                } else if (phoneType == Phone.PHONE_TYPE_GSM) {
2327c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    return gsmGetClccResult();
232879b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                } else {
232979b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                    throw new IllegalStateException("Unexpected phone type: " + phoneType);
2330c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                }
2331b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2332b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2333b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2334b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Call Hold and Multiparty Handling command
2335b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CHLD", new AtCommandHandler() {
2336b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2337b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
23388058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                int phoneType = phone.getPhoneType();
23398058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                Call ringingCall = mCM.getFirstActiveRingingCall();
23408058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                Call backgroundCall = mCM.getFirstActiveBgCall();
23418058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang
2342b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length >= 1) {
2343b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (args[0].equals(0)) {
2344b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        boolean result;
23458058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                        if (ringingCall.isRinging()) {
23468058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                            result = PhoneUtils.hangupRingingCall(ringingCall);
2347b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        } else {
23488058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                            result = PhoneUtils.hangupHoldingCall(backgroundCall);
2349b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        }
2350b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        if (result) {
2351b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            return new AtCommandResult(AtCommandResult.OK);
2352b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        } else {
2353b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            return new AtCommandResult(AtCommandResult.ERROR);
2354b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        }
2355b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    } else if (args[0].equals(1)) {
235679b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        if (phoneType == Phone.PHONE_TYPE_CDMA) {
23578058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                            if (ringingCall.isRinging()) {
235891f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                // Hangup the active call and then answer call waiting call.
23598eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan                                if (VDBG) log("CHLD:1 Callwaiting Answer call");
2360ba2b1409db0b7f7901bd30c238f7a63c72dc0a35jhtop.kim                                PhoneUtils.hangupRingingAndActive(phone);
2361c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            } else {
2362c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                // If there is no Call waiting then just hangup
2363c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                // the active call. In CDMA this mean that the complete
2364c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                // call session would be ended
23658eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan                                if (VDBG) log("CHLD:1 Hangup Call");
23668343169cc89621d46dce86449f5ee1ff5d3a4919John Wang                                PhoneUtils.hangup(PhoneApp.getInstance().mCM);
2367c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            }
2368b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            return new AtCommandResult(AtCommandResult.OK);
236979b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        } else if (phoneType == Phone.PHONE_TYPE_GSM) {
2370a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                            // Hangup active call, answer held call
23718343169cc89621d46dce86449f5ee1ff5d3a4919John Wang                            if (PhoneUtils.answerAndEndActive(
23728058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                                    PhoneApp.getInstance().mCM, ringingCall)) {
2373a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                                return new AtCommandResult(AtCommandResult.OK);
2374a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                            } else {
2375a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                                return new AtCommandResult(AtCommandResult.ERROR);
2376a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                            }
237779b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        } else {
237879b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                            throw new IllegalStateException("Unexpected phone type: " + phoneType);
2379b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        }
2380b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    } else if (args[0].equals(2)) {
238191f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                        sendURC("OK");
238279b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        if (phoneType == Phone.PHONE_TYPE_CDMA) {
2383a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                            // For CDMA, the way we switch to a new incoming call is by
2384a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                            // calling PhoneUtils.answerCall(). switchAndHoldActive() won't
2385a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                            // properly update the call state within telephony.
2386c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            // If the Phone state is already in CONF_CALL then we simply send
2387c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            // a flash cmd by calling switchHoldingAndActive()
23888058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                            if (ringingCall.isRinging()) {
23898eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan                                if (VDBG) log("CHLD:2 Callwaiting Answer call");
23908058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                                PhoneUtils.answerCall(ringingCall);
2391a50e10e2efadac960987eaadc0938c6f92d3ee90John Wang                                PhoneUtils.setMute(false);
2392c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                // Setting the second callers state flag to TRUE (i.e. active)
2393c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                cdmaSetSecondCallState(true);
2394c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            } else if (PhoneApp.getInstance().cdmaPhoneCallState
2395c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                    .getCurrentCallState()
2396c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                    == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
23978eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan                                if (VDBG) log("CHLD:2 Swap Calls");
23988058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                                PhoneUtils.switchHoldingAndActive(backgroundCall);
2399c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                // Toggle the second callers active state flag
2400c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                                cdmaSwapSecondCallState();
2401c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            }
240279b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        } else if (phoneType == Phone.PHONE_TYPE_GSM) {
24038058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                            PhoneUtils.switchHoldingAndActive(backgroundCall);
240479b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        } else {
240579b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                            throw new IllegalStateException("Unexpected phone type: " + phoneType);
2406a76113dcb0def438d0b3019aeb88fdbfbfbe90d4Paul Berman                        }
240791f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                        return new AtCommandResult(AtCommandResult.UNSOLICITED);
2408b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    } else if (args[0].equals(3)) {
240991f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                        sendURC("OK");
241079b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        if (phoneType == Phone.PHONE_TYPE_CDMA) {
241191f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                            CdmaPhoneCallState.PhoneCallState state =
241291f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                PhoneApp.getInstance().cdmaPhoneCallState.getCurrentCallState();
2413c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            // For CDMA, we need to check if the call is in THRWAY_ACTIVE state
241491f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                            if (state == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
24158eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan                                if (VDBG) log("CHLD:3 Merge Calls");
2416b045b9344f339170d134cf814357361d96c349cfHung-ying Tyan                                PhoneUtils.mergeCalls();
241791f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                            } else if (state == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
241891f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                // State is CONF_CALL already and we are getting a merge call
241991f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                // This can happen when CONF_CALL was entered from a Call Waiting
242091f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                                mBluetoothPhoneState.updateCallHeld();
2421c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            }
242279b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        } else if (phoneType == Phone.PHONE_TYPE_GSM) {
24238058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                            if (mCM.hasActiveFgCall() && mCM.hasActiveBgCall()) {
2424b045b9344f339170d134cf814357361d96c349cfHung-ying Tyan                                PhoneUtils.mergeCalls();
2425c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                            }
242679b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                        } else {
242779b9f6e30e8402b103f1ec3ed9dda1fcad2b1cd5Tammo Spalink                            throw new IllegalStateException("Unexpected phone type: " + phoneType);
2428b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        }
242991f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh                        return new AtCommandResult(AtCommandResult.UNSOLICITED);
2430b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2431b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2432b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.ERROR);
2433b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2434b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2435b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleTestCommand() {
24360966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                mServiceConnectionEstablished = true;
2437d2b5e78809bc2532b2377ba2c351d48710097c0cNick Pelly                sendURC("+CHLD: (0,1,2,3)");
2438d2b5e78809bc2532b2377ba2c351d48710097c0cNick Pelly                sendURC("OK");  // send reply first, then connect audio
24390966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                if (isIncallAudio()) {
24400966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                    audioOn();
244134a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi                } else if (mCM.getFirstActiveRingingCall().isRinging()) {
244234a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi                    // need to update HS with RING when single ringing call exist
244334a30a05feb48e56b8f6d5e05d9d58ce649bfdd1Srinivas Krovvidi                    mBluetoothPhoneState.ring();
24440966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                }
24450966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                // already replied
24460966f42d45611b961635c8d23fc4a4df0f5e29c4Nick Pelly                return new AtCommandResult(AtCommandResult.UNSOLICITED);
2447b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2448b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2449b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2450b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Get Network operator name
2451b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+COPS", new AtCommandHandler() {
2452b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2453b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
24548058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                String operatorName = phone.getServiceState().getOperatorAlphaLong();
2455b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (operatorName != null) {
2456b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (operatorName.length() > 16) {
2457b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        operatorName = operatorName.substring(0, 16);
2458b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2459b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(
2460b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            "+COPS: 0,0,\"" + operatorName + "\"");
2461b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
2462b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(
24635b58955238953dd3c689b1d1342cb9d79ec3e9deJesper Hansson                            "+COPS: 0");
2464b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2465b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2466b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2467b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2468b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Handsfree only supports AT+COPS=3,0
2469b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length != 2 || !(args[0] instanceof Integer)
2470b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    || !(args[1] instanceof Integer)) {
2471b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    // syntax error
2472b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.ERROR);
2473b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else if ((Integer)args[0] != 3 || (Integer)args[1] != 0) {
2474b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return reportCmeError(BluetoothCmeError.OPERATION_NOT_SUPPORTED);
2475b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
2476b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.OK);
2477b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2478b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2479b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2480b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleTestCommand() {
2481b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Out of spec, but lets be friendly
2482b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+COPS: (3),(0)");
2483b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2484b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2485b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2486b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Mobile PIN
2487b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // AT+CPIN is not in the handsfree spec (although it is in 3GPP)
2488b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CPIN", new AtCommandHandler() {
2489b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2490b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
2491b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CPIN: READY");
2492b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2493b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2494b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2495b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Bluetooth Response and Hold
2496b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Only supported on PDC (Japan) and CDMA networks.
2497b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+BTRH", new AtCommandHandler() {
2498b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2499b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
2500b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Replying with just OK indicates no response and hold
2501b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // features in use now
2502b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.OK);
2503b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2504b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2505b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2506b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Neeed PDC or CDMA
2507b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.ERROR);
2508b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2509b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2510b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2511b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Request International Mobile Subscriber Identity (IMSI)
2512b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Not in bluetooth handset spec
2513b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CIMI", new AtCommandHandler() {
2514b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2515b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2516b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // AT+CIMI
25178058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                String imsi = phone.getSubscriberId();
2518b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (imsi == null || imsi.length() == 0) {
2519b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return reportCmeError(BluetoothCmeError.SIM_FAILURE);
2520b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
2521b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(imsi);
2522b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2523b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2524b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2525b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2526b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Calling Line Identification Presentation
2527b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CLIP", new AtCommandHandler() {
2528b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2529b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleReadCommand() {
2530b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Currently assumes the network is provisioned for CLIP
2531b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CLIP: " + (mClip ? "1" : "0") + ",1");
2532b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2533b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2534b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2535b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // AT+CLIP=<n>
2536b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length >= 1 && (args[0].equals(0) || args[0].equals(1))) {
2537b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    mClip = args[0].equals(1);
2538b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.OK);
2539b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
2540b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.ERROR);
2541b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2542b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2543b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2544b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleTestCommand() {
2545b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CLIP: (0-1)");
2546b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2547b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2548b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2549b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // AT+CGSN - Returns the device IMEI number.
2550b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CGSN", new AtCommandHandler() {
2551b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2552b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2553b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Get the IMEI of the device.
25548058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                // phone will not be NULL at this point.
25558058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                return new AtCommandResult("+CGSN: " + phone.getDeviceId());
2556b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2557b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2558b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2559b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // AT+CGMM - Query Model Information
2560b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CGMM", new AtCommandHandler() {
2561b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2562b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2563b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Return the Model Information.
2564b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                String model = SystemProperties.get("ro.product.model");
2565b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (model != null) {
2566b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult("+CGMM: " + model);
2567b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
2568b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.ERROR);
2569b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2570b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2571b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2572b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2573b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // AT+CGMI - Query Manufacturer Information
2574b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CGMI", new AtCommandHandler() {
2575b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2576b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2577b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Return the Model Information.
2578b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                String manuf = SystemProperties.get("ro.product.manufacturer");
2579b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (manuf != null) {
2580b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult("+CGMI: " + manuf);
2581b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else {
2582b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.ERROR);
2583b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2584b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2585b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2586b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2587b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Noise Reduction and Echo Cancellation control
2588b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+NREC", new AtCommandHandler() {
2589b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2590b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2591b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args[0].equals(0)) {
2592aa23e1c3c758bad23d8b6709147cc1ff7cd1e43cEric Laurent                    mAudioManager.setParameters(HEADSET_NREC+"=off");
2593b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.OK);
2594b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else if (args[0].equals(1)) {
2595aa23e1c3c758bad23d8b6709147cc1ff7cd1e43cEric Laurent                    mAudioManager.setParameters(HEADSET_NREC+"=on");
2596b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.OK);
2597b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2598b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.ERROR);
2599b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2600b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2601b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2602b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Voice recognition (dialing)
2603b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+BVRA", new AtCommandHandler() {
2604b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2605b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2606b8dbab241df3aa3487c1bdb488fd4e0b694d2d9aEric Laurent                if (!BluetoothHeadset.isBluetoothVoiceDialingEnabled(mContext)) {
26076967e2d953bc077c99c4831946201f3d333b833fNick Pelly                    return new AtCommandResult(AtCommandResult.ERROR);
26086967e2d953bc077c99c4831946201f3d333b833fNick Pelly                }
2609b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length >= 1 && args[0].equals(1)) {
2610b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    synchronized (BluetoothHandsfree.this) {
26111c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent                        if (!isVoiceRecognitionInProgress() &&
2612eb1d4e290a13b55430960fd7ba58404608f10593Eric Laurent                            !isCellularCallInProgress() &&
2613eb1d4e290a13b55430960fd7ba58404608f10593Eric Laurent                            !isVirtualCallInProgress()) {
2614b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            try {
2615b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                                mContext.startActivity(sVoiceCommandIntent);
2616b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            } catch (ActivityNotFoundException e) {
2617b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                                return new AtCommandResult(AtCommandResult.ERROR);
2618b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            }
2619b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                            expectVoiceRecognition();
2620b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        }
2621b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
2622b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.UNSOLICITED);  // send nothing yet
2623b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } else if (args.length >= 1 && args[0].equals(0)) {
26241c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent                    if (isVoiceRecognitionInProgress()) {
26251c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent                        audioOff();
26261c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent                    }
2627b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.OK);
2628b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2629b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.ERROR);
2630b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2631b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2632b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleTestCommand() {
26336967e2d953bc077c99c4831946201f3d333b833fNick Pelly                return new AtCommandResult("+BVRA: (0-1)");
2634b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2635b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2636b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2637b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Retrieve Subscriber Number
2638b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CNUM", new AtCommandHandler() {
2639b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2640b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
26418058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                String number = phone.getLine1Number();
2642b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (number == null) {
2643b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.OK);
2644b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2645b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CNUM: ,\"" + number + "\"," +
2646b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        PhoneNumberUtils.toaFromString(number) + ",,4");
2647b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2648b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2649b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2650b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Microphone Gain
2651b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+VGM", new AtCommandHandler() {
2652b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2653b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2654b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // AT+VGM=<gain>    in range [0,15]
2655b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // Headset/Handsfree is reporting its current gain setting
2656b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.OK);
2657b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2658b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2659b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2660b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Speaker Gain
2661b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+VGS", new AtCommandHandler() {
2662b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2663b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleSetCommand(Object[] args) {
2664b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                // AT+VGS=<gain>    in range [0,15]
2665b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (args.length != 1 || !(args[0] instanceof Integer)) {
2666b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    return new AtCommandResult(AtCommandResult.ERROR);
2667b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2668b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mScoGain = (Integer) args[0];
2669b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                int flag =  mAudioManager.isBluetoothScoOn() ? AudioManager.FLAG_SHOW_UI:0;
2670b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2671b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, mScoGain, flag);
2672b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult(AtCommandResult.OK);
2673b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2674b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
2675b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2676b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        // Phone activity status
2677b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        parser.register("+CPAS", new AtCommandHandler() {
2678b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            @Override
2679b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            public AtCommandResult handleActionCommand() {
2680b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                int status = 0;
26818058bbc4c7d0d56e133bd2fb9dfc559004e4d61aJohn Wang                switch (mCM.getState()) {
2682b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                case IDLE:
2683b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    status = 0;
2684b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    break;
2685b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                case RINGING:
2686b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    status = 3;
2687b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    break;
2688b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                case OFFHOOK:
2689b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    status = 4;
2690b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    break;
2691b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2692b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                return new AtCommandResult("+CPAS: " + status);
2693b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2694b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        });
26951498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
2696b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mPhonebook.register(parser);
2697b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2698b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2699b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public void sendScoGainUpdate(int gain) {
2700b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (mScoGain != gain && (mRemoteBrsf & BRSF_HF_REMOTE_VOL_CONTROL) != 0x0) {
2701b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sendURC("+VGS:" + gain);
2702b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mScoGain = gain;
2703b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2704b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2705b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2706b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    public AtCommandResult reportCmeError(int error) {
2707b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (mCmee) {
2708b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            AtCommandResult result = new AtCommandResult(AtCommandResult.UNSOLICITED);
2709b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            result.addResponse("+CME ERROR: " + error);
2710b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return result;
2711b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        } else {
2712b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            return new AtCommandResult(AtCommandResult.ERROR);
2713b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2714b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2715b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2716b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final int START_CALL_TIMEOUT = 10000;  // ms
2717b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2718b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private synchronized void expectCallStart() {
2719b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mWaitingForCallStart = true;
2720b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        Message msg = Message.obtain(mHandler, CHECK_CALL_STARTED);
2721b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mHandler.sendMessageDelayed(msg, START_CALL_TIMEOUT);
2722b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (!mStartCallWakeLock.isHeld()) {
2723b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mStartCallWakeLock.acquire(START_CALL_TIMEOUT);
2724b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2725b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2726b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2727b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private synchronized void callStarted() {
2728b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (mWaitingForCallStart) {
2729b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mWaitingForCallStart = false;
2730b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sendURC("OK");
2731b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            if (mStartCallWakeLock.isHeld()) {
2732b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                mStartCallWakeLock.release();
2733b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
2734b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2735b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2736b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2737b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static final int START_VOICE_RECOGNITION_TIMEOUT = 5000;  // ms
2738b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2739b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private synchronized void expectVoiceRecognition() {
2740b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mWaitingForVoiceRecognition = true;
2741b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        Message msg = Message.obtain(mHandler, CHECK_VOICE_RECOGNITION_STARTED);
2742b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        mHandler.sendMessageDelayed(msg, START_VOICE_RECOGNITION_TIMEOUT);
2743b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (!mStartVoiceRecognitionWakeLock.isHeld()) {
2744b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mStartVoiceRecognitionWakeLock.acquire(START_VOICE_RECOGNITION_TIMEOUT);
2745b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2746b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2747b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2748b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ synchronized boolean startVoiceRecognition() {
27491c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
27501c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        if  ((isCellularCallInProgress()) ||
27511c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent             (isVirtualCallInProgress()) ||
27521c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent             mVoiceRecognitionStarted) {
27531c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent            Log.e(TAG, "startVoiceRecognition: Call in progress");
27541c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent            return false;
27551c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        }
27561c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
27571c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        mVoiceRecognitionStarted = true;
27581c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
2759b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (mWaitingForVoiceRecognition) {
2760b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // HF initiated
2761b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mWaitingForVoiceRecognition = false;
2762b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sendURC("OK");
2763b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        } else {
2764b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            // AG initiated
2765b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            sendURC("+BVRA: 1");
2766b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2767b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        boolean ret = audioOn();
27681c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        if (ret == false) {
27691c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent            mVoiceRecognitionStarted = false;
27701c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        }
2771b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (mStartVoiceRecognitionWakeLock.isHeld()) {
2772b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mStartVoiceRecognitionWakeLock.release();
2773b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2774b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return ret;
2775b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2776b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2777b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /* package */ synchronized boolean stopVoiceRecognition() {
27781c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
27791c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        if (!isVoiceRecognitionInProgress()) {
27801c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent            return false;
27811c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        }
27821c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
27831c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        mVoiceRecognitionStarted = false;
27841c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
2785b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        sendURC("+BVRA: 0");
2786b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        audioOff();
2787b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return true;
2788b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2789b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
27901c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent    // Voice Recognition in Progress
27911c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent    private boolean isVoiceRecognitionInProgress() {
27921c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent        return (mVoiceRecognitionStarted || mWaitingForVoiceRecognition);
27931c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent    }
27941c5ea6e6845b1e81b987a33e0d5fd3944552d7edEric Laurent
27951498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    /*
27961498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     * This class broadcasts vendor-specific commands + arguments to interested receivers.
27971498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek     */
27981498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    private class VendorSpecificCommandHandler extends AtCommandHandler {
27991498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
28001498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        private String mCommandName;
28011498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
28021498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        private int mCompanyId;
28031498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
28041498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        private VendorSpecificCommandHandler(String commandName, int companyId) {
28051498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek            mCommandName = commandName;
28061498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek            mCompanyId = companyId;
28071498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        }
28081498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
28091498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        @Override
281086324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        public AtCommandResult handleReadCommand() {
281186324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh            return new AtCommandResult(AtCommandResult.ERROR);
281286324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        }
281386324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh
281486324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        @Override
281586324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        public AtCommandResult handleTestCommand() {
281686324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh            return new AtCommandResult(AtCommandResult.ERROR);
281786324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        }
281886324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh
281986324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        @Override
282086324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        public AtCommandResult handleActionCommand() {
282186324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh            return new AtCommandResult(AtCommandResult.ERROR);
282286324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        }
282386324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh
282486324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh        @Override
28251498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        public AtCommandResult handleSetCommand(Object[] arguments) {
28261498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek            broadcastVendorSpecificEventIntent(mCommandName,
28271498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                               mCompanyId,
282886324d3be365c1104c40ba007e671de8f1306021Jaikumar Ganesh                                               BluetoothHeadset.AT_CMD_TYPE_SET,
28291498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                               arguments,
28301498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek                                               mHeadset.getRemoteDevice());
28311498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek            return new AtCommandResult(AtCommandResult.OK);
28321498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek        }
28331498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek    }
28341498a77b9d76318817fe5a1e3a961070ead26fadHerb Jellinek
2835b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean inDebug() {
2836b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return DBG && SystemProperties.getBoolean(DebugThread.DEBUG_HANDSFREE, false);
2837b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2838b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2839b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private boolean allowAudioAnytime() {
2840b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        return inDebug() && SystemProperties.getBoolean(DebugThread.DEBUG_HANDSFREE_AUDIO_ANYTIME,
2841b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                false);
2842b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2843b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2844b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private void startDebug() {
2845b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (DBG && mDebugThread == null) {
2846b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mDebugThread = new DebugThread();
2847b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mDebugThread.start();
2848b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2849b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2850b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2851b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private void stopDebug() {
2852b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        if (mDebugThread != null) {
2853b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mDebugThread.interrupt();
2854b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            mDebugThread = null;
2855b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
2856b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
2857b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2858b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    // VirtualCall SCO support
2859b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    //
2860b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2861b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    // Cellular call in progress
2862b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    private boolean isCellularCallInProgress() {
2863b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        if (mCM.hasActiveFgCall() || mCM.hasActiveRingingCall()) return true;
2864b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        return false;
2865b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    }
2866b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2867b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    // Virtual Call in Progress
2868b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    private boolean isVirtualCallInProgress() {
2869b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        return mVirtualCallStarted;
2870b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    }
2871b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2872a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh    void setVirtualCallInProgress(boolean state) {
2873a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        mVirtualCallStarted = state;
2874a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh    }
2875a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh
2876b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    //NOTE: Currently the VirtualCall API does not allow the application to initiate a call
2877b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    // transfer. Call transfer may be initiated from the handsfree device and this is handled by
2878b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    // the VirtualCall API
2879a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh    synchronized boolean initiateScoUsingVirtualVoiceCall() {
2880a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        if (DBG) log("initiateScoUsingVirtualVoiceCall: Received");
2881b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        // 1. Check if the SCO state is idle
2882a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        if (isCellularCallInProgress() || isVoiceRecognitionInProgress()) {
2883a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh            Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress");
2884b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            return false;
2885b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        }
2886b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2887b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        // 2. Perform outgoing call setup procedure
2888a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        if (mBluetoothPhoneState.sendUpdate() && !isVirtualCallInProgress()) {
2889b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            AtCommandResult result = new AtCommandResult(AtCommandResult.UNSOLICITED);
2890b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            // outgoing call
2891b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            result.addResponse("+CIEV: 3,2");
2892b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            result.addResponse("+CIEV: 2,1");
2893b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            result.addResponse("+CIEV: 3,0");
2894b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            sendURC(result.toString());
2895a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh            if (DBG) Log.d(TAG, "initiateScoUsingVirtualVoiceCall: Sent Call-setup procedure");
2896b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        }
2897a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh
28986e979d3698decc3631570a4f7db483cb94262c5dJaikumar Ganesh        mVirtualCallStarted = true;
28996e979d3698decc3631570a4f7db483cb94262c5dJaikumar Ganesh
2900b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        // 3. Open the Audio Connection
2901b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        if (audioOn() == false) {
2902a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh            log("initiateScoUsingVirtualVoiceCall: audioON failed");
2903a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh            terminateScoUsingVirtualVoiceCall();
2904b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            return false;
2905b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        }
2906b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2907b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        mAudioPossible = true;
2908b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2909b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        // Done
2910a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        if (DBG) log("initiateScoUsingVirtualVoiceCall: Done");
2911b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        return true;
2912b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    }
2913b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2914a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh    synchronized boolean terminateScoUsingVirtualVoiceCall() {
2915a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        if (DBG) log("terminateScoUsingVirtualVoiceCall: Received");
29169760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie
29179760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie        if (!isVirtualCallInProgress()) {
29189760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie            return false;
29199760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie        }
29209760482b3e3f8a0c11cae2866ff03fab1b2c3242Matthew Xie
2921a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        // 1. Release audio connection
2922b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        audioOff();
2923b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2924a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        // 2. terminate call-setup
29256e979d3698decc3631570a4f7db483cb94262c5dJaikumar Ganesh        if (mBluetoothPhoneState.sendUpdate()) {
2926b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            AtCommandResult result = new AtCommandResult(AtCommandResult.UNSOLICITED);
2927b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            // outgoing call
2928b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            result.addResponse("+CIEV: 2,0");
2929b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh            sendURC(result.toString());
2930a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh            if (DBG) log("terminateScoUsingVirtualVoiceCall: Sent Call-setup procedure");
2931b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        }
29326e979d3698decc3631570a4f7db483cb94262c5dJaikumar Ganesh        mVirtualCallStarted = false;
2933b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        mAudioPossible = false;
2934b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2935b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        // Done
2936a8b5afa3f830a05fd6679642a6a29dd3265cd273Jaikumar Ganesh        if (DBG) log("terminateScoUsingVirtualVoiceCall: Done");
2937b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh        return true;
2938b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh    }
2939b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2940b5d4288b3b4f336601bf6ebf16861d399a4d83a9Jaikumar Ganesh
2941b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    /** Debug thread to read debug properties - runs when debug.bt.hfp is true
2942b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     *  at the time a bluetooth handsfree device is connected. Debug properties
2943b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project     *  are polled and mock updates sent every 1 second */
2944b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private class DebugThread extends Thread {
2945b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** Turns on/off handsfree profile debugging mode */
294677c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_HANDSFREE = "debug.bt.hfp";
2947b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2948b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** Mock battery level change - use 0 to 5 */
294977c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_HANDSFREE_BATTERY = "debug.bt.hfp.battery";
2950b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2951b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** Mock no cellular service when false */
295277c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_HANDSFREE_SERVICE = "debug.bt.hfp.service";
2953b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2954b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** Mock cellular roaming when true */
295577c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_HANDSFREE_ROAM = "debug.bt.hfp.roam";
2956b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2957b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** false to true transition will force an audio (SCO) connection to
2958b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         *  be established. true to false will force audio to be disconnected
2959b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         */
296077c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_HANDSFREE_AUDIO = "debug.bt.hfp.audio";
2961b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2962b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** true allows incoming SCO connection out of call.
2963b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         */
296477c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_HANDSFREE_AUDIO_ANYTIME = "debug.bt.hfp.audio_anytime";
2965b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2966b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** Mock signal strength change in ASU - use 0 to 31 */
296777c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_HANDSFREE_SIGNAL = "debug.bt.hfp.signal";
2968b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2969b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** Debug AT+CLCC: print +CLCC result */
297077c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_HANDSFREE_CLCC = "debug.bt.hfp.clcc";
2971b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2972b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        /** Debug AT+BSIR - Send In Band Ringtones Unsolicited AT command.
2973b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * debug.bt.unsol.inband = 0 => AT+BSIR = 0 sent by the AG
2974b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * debug.bt.unsol.inband = 1 => AT+BSIR = 0 sent by the AG
2975b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         * Other values are ignored.
2976b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project         */
2977b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
297877c123c48996dbecf307b4e75a05b0d4a5561563Conley Owens        static final String DEBUG_UNSOL_INBAND_RINGTONE = "debug.bt.unsol.inband";
2979b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2980b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        @Override
2981b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        public void run() {
2982b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            boolean oldService = true;
2983b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            boolean oldRoam = false;
2984b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            boolean oldAudio = false;
2985b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2986b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            while (!isInterrupted() && inDebug()) {
2987b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                int batteryLevel = SystemProperties.getInt(DEBUG_HANDSFREE_BATTERY, -1);
2988b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (batteryLevel >= 0 && batteryLevel <= 5) {
2989b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    Intent intent = new Intent();
2990b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    intent.putExtra("level", batteryLevel);
2991b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    intent.putExtra("scale", 5);
29921dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                    mBluetoothPhoneState.updateBatteryState(intent);
2993b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
2994b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
2995b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                boolean serviceStateChanged = false;
2996b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (SystemProperties.getBoolean(DEBUG_HANDSFREE_SERVICE, true) != oldService) {
2997b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    oldService = !oldService;
2998b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    serviceStateChanged = true;
2999b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
3000b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (SystemProperties.getBoolean(DEBUG_HANDSFREE_ROAM, false) != oldRoam) {
3001b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    oldRoam = !oldRoam;
3002b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    serviceStateChanged = true;
3003b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
3004b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (serviceStateChanged) {
3005b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    Bundle b = new Bundle();
3006b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    b.putInt("state", oldService ? 0 : 1);
3007b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    b.putBoolean("roaming", oldRoam);
30081dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                    mBluetoothPhoneState.updateServiceState(true, ServiceState.newFromBundle(b));
3009b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
3010b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
3011b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (SystemProperties.getBoolean(DEBUG_HANDSFREE_AUDIO, false) != oldAudio) {
3012b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    oldAudio = !oldAudio;
3013b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    if (oldAudio) {
3014b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        audioOn();
3015b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    } else {
3016b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        audioOff();
3017b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    }
3018b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
3019b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
3020b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                int signalLevel = SystemProperties.getInt(DEBUG_HANDSFREE_SIGNAL, -1);
3021b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (signalLevel >= 0 && signalLevel <= 31) {
3022404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                    SignalStrength signalStrength = new SignalStrength(signalLevel, -1, -1, -1,
3023404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                            -1, -1, -1, true);
3024b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    Intent intent = new Intent();
3025404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                    Bundle data = new Bundle();
3026404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                    signalStrength.fillInNotifierBundle(data);
3027404edc94de563aef5fd5ba48be9114a970cb93bbWink Saville                    intent.putExtras(data);
30281dad0387bf2f280c12bdac172604e9779134fbcdJaikumar Ganesh                    mBluetoothPhoneState.updateSignalState(intent);
3029b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
3030b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
3031b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (SystemProperties.getBoolean(DEBUG_HANDSFREE_CLCC, false)) {
3032c9d9ed30aa547b79b81adc13a4d148a003b6ee62w                    log(gsmGetClccResult().toString());
3033b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
3034b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                try {
3035b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    sleep(1000);  // 1 second
3036b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                } catch (InterruptedException e) {
3037b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    break;
3038b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
3039b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
3040b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                int inBandRing =
3041b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    SystemProperties.getInt(DEBUG_UNSOL_INBAND_RINGTONE, -1);
3042b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                if (inBandRing == 0 || inBandRing == 1) {
3043b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    AtCommandResult result =
3044b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                        new AtCommandResult(AtCommandResult.UNSOLICITED);
3045b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    result.addResponse("+BSIR: " + inBandRing);
3046b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                    sendURC(result.toString());
3047b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project                }
3048b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project            }
3049b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        }
3050b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
3051b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project
3052c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    public void cdmaSwapSecondCallState() {
30538eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan        if (VDBG) log("cdmaSetSecondCallState: Toggling mCdmaIsSecondCallActive");
3054c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        mCdmaIsSecondCallActive = !mCdmaIsSecondCallActive;
305591f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh        mCdmaCallsSwapped = true;
3056c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    }
3057c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
3058c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    public void cdmaSetSecondCallState(boolean state) {
30598eb32ddb60cfe1a0d0733735c30ecfc814452806Jackson Fan        if (VDBG) log("cdmaSetSecondCallState: Setting mCdmaIsSecondCallActive to " + state);
3060c9d9ed30aa547b79b81adc13a4d148a003b6ee62w        mCdmaIsSecondCallActive = state;
306191f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh
306291f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh        if (!mCdmaIsSecondCallActive) {
306391f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh            mCdmaCallsSwapped = false;
306491f4a3ccc2ac2e146f54c4b4b88b62c16595602aJaikumar Ganesh        }
3065c9d9ed30aa547b79b81adc13a4d148a003b6ee62w    }
3066c9d9ed30aa547b79b81adc13a4d148a003b6ee62w
3067b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    private static void log(String msg) {
3068b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project        Log.d(TAG, msg);
3069b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project    }
3070b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project}
3071