1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /**
18  * Bluetooth Handset StateMachine
19  *                      (Disconnected)
20  *                           |    ^
21  *                   CONNECT |    | DISCONNECTED
22  *                           V    |
23  *                         (Pending)
24  *                           |    ^
25  *                 CONNECTED |    | CONNECT
26  *                           V    |
27  *                        (Connected)
28  *                           |    ^
29  *             CONNECT_AUDIO |    | DISCONNECT_AUDIO
30  *                           V    |
31  *                         (AudioOn)
32  */
33 package com.android.bluetooth.hfp;
34 
35 import android.bluetooth.BluetoothAdapter;
36 import android.bluetooth.BluetoothAssignedNumbers;
37 import android.bluetooth.BluetoothDevice;
38 import android.bluetooth.BluetoothHeadset;
39 import android.bluetooth.BluetoothProfile;
40 import android.bluetooth.BluetoothUuid;
41 import android.bluetooth.IBluetoothHeadsetPhone;
42 import android.content.ComponentName;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.ServiceConnection;
46 import android.content.ActivityNotFoundException;
47 import android.media.AudioManager;
48 import android.net.Uri;
49 import android.os.IBinder;
50 import android.os.IDeviceIdleController;
51 import android.os.Message;
52 import android.os.ParcelUuid;
53 import android.os.RemoteException;
54 import android.os.ServiceManager;
55 import android.os.PowerManager;
56 import android.os.UserHandle;
57 import android.os.PowerManager.WakeLock;
58 import android.telephony.PhoneNumberUtils;
59 import android.util.Log;
60 import com.android.bluetooth.Utils;
61 import com.android.bluetooth.btservice.AdapterService;
62 import com.android.bluetooth.btservice.ProfileService;
63 import com.android.internal.util.IState;
64 import com.android.internal.util.State;
65 import com.android.internal.util.StateMachine;
66 import java.util.ArrayList;
67 import java.util.HashMap;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Set;
71 import android.os.SystemProperties;
72 
73 final class HeadsetStateMachine extends StateMachine {
74     private static final String TAG = "HeadsetStateMachine";
75     private static final boolean DBG = false;
76     // For Debugging only
77     private static int sRefCount = 0;
78 
79     private static final String HEADSET_NAME = "bt_headset_name";
80     private static final String HEADSET_NREC = "bt_headset_nrec";
81     private static final String HEADSET_WBS = "bt_wbs";
82 
83     static final int CONNECT = 1;
84     static final int DISCONNECT = 2;
85     static final int CONNECT_AUDIO = 3;
86     static final int DISCONNECT_AUDIO = 4;
87     static final int VOICE_RECOGNITION_START = 5;
88     static final int VOICE_RECOGNITION_STOP = 6;
89 
90     // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION
91     // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO
92     static final int INTENT_SCO_VOLUME_CHANGED = 7;
93     static final int SET_MIC_VOLUME = 8;
94     static final int CALL_STATE_CHANGED = 9;
95     static final int INTENT_BATTERY_CHANGED = 10;
96     static final int DEVICE_STATE_CHANGED = 11;
97     static final int SEND_CCLC_RESPONSE = 12;
98     static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 13;
99 
100     static final int VIRTUAL_CALL_START = 14;
101     static final int VIRTUAL_CALL_STOP = 15;
102 
103     static final int ENABLE_WBS = 16;
104     static final int DISABLE_WBS = 17;
105 
106     static final int BIND_RESPONSE = 18;
107 
108     private static final int STACK_EVENT = 101;
109     private static final int DIALING_OUT_TIMEOUT = 102;
110     private static final int START_VR_TIMEOUT = 103;
111     private static final int CLCC_RSP_TIMEOUT = 104;
112 
113     private static final int CONNECT_TIMEOUT = 201;
114 
115     private static final int DIALING_OUT_TIMEOUT_VALUE = 10000;
116     private static final int START_VR_TIMEOUT_VALUE = 5000;
117     private static final int CLCC_RSP_TIMEOUT_VALUE = 5000;
118 
119     // Max number of HF connections at any time
120     private int max_hf_connections = 1;
121 
122     private static final int NBS_CODEC = 1;
123     private static final int WBS_CODEC = 2;
124 
125     // Keys are AT commands, and values are the company IDs.
126     private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID;
127     // Hash for storing the Audio Parameters like NREC for connected headsets
128     private HashMap<BluetoothDevice, HashMap> mHeadsetAudioParam =
129             new HashMap<BluetoothDevice, HashMap>();
130     // Hash for storing the Remotedevice BRSF
131     private HashMap<BluetoothDevice, Integer> mHeadsetBrsf =
132             new HashMap<BluetoothDevice, Integer>();
133 
134     private static final ParcelUuid[] HEADSET_UUIDS = {
135             BluetoothUuid.HSP, BluetoothUuid.Handsfree,
136     };
137 
138     private Disconnected mDisconnected;
139     private Pending mPending;
140     private Connected mConnected;
141     private AudioOn mAudioOn;
142     // Multi HFP: add new class object
143     private MultiHFPending mMultiHFPending;
144 
145     private HeadsetService mService;
146     private PowerManager mPowerManager;
147     private boolean mVirtualCallStarted = false;
148     private boolean mVoiceRecognitionStarted = false;
149     private boolean mWaitingForVoiceRecognition = false;
150     private WakeLock mStartVoiceRecognitionWakeLock; // held while waiting for voice recognition
151 
152     private boolean mDialingOut = false;
153     private AudioManager mAudioManager;
154     private AtPhonebook mPhonebook;
155 
156     private static Intent sVoiceCommandIntent;
157 
158     private HeadsetPhoneState mPhoneState;
159     private int mAudioState;
160     private BluetoothAdapter mAdapter;
161     private IBluetoothHeadsetPhone mPhoneProxy;
162     private boolean mNativeAvailable;
163 
164     // Indicates whether audio can be routed to the device.
165     private boolean mAudioRouteAllowed = true;
166 
167     // Indicate whether service level connection has been established for this device
168     private boolean mSlcConnected = false;
169 
170     // mCurrentDevice is the device connected before the state changes
171     // mTargetDevice is the device to be connected
172     // mIncomingDevice is the device connecting to us, valid only in Pending state
173     //                when mIncomingDevice is not null, both mCurrentDevice
174     //                  and mTargetDevice are null
175     //                when either mCurrentDevice or mTargetDevice is not null,
176     //                  mIncomingDevice is null
177     // Stable states
178     //   No connection, Disconnected state
179     //                  both mCurrentDevice and mTargetDevice are null
180     //   Connected, Connected state
181     //              mCurrentDevice is not null, mTargetDevice is null
182     // Interim states
183     //   Connecting to a device, Pending
184     //                           mCurrentDevice is null, mTargetDevice is not null
185     //   Disconnecting device, Connecting to new device
186     //     Pending
187     //     Both mCurrentDevice and mTargetDevice are not null
188     //   Disconnecting device Pending
189     //                        mCurrentDevice is not null, mTargetDevice is null
190     //   Incoming connections Pending
191     //                        Both mCurrentDevice and mTargetDevice are null
192     private BluetoothDevice mCurrentDevice = null;
193     private BluetoothDevice mTargetDevice = null;
194     private BluetoothDevice mIncomingDevice = null;
195     private BluetoothDevice mActiveScoDevice = null;
196     private BluetoothDevice mMultiDisconnectDevice = null;
197 
198     // Multi HFP: Connected devices list holds all currently connected headsets
199     private ArrayList<BluetoothDevice> mConnectedDevicesList = new ArrayList<BluetoothDevice>();
200 
201     static {
classInitNative()202         classInitNative();
203 
204         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<String, Integer>();
205         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XEVENT", BluetoothAssignedNumbers.PLANTRONICS);
206         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+ANDROID", BluetoothAssignedNumbers.GOOGLE);
207     }
208 
HeadsetStateMachine(HeadsetService context)209     private HeadsetStateMachine(HeadsetService context) {
210         super(TAG);
211         mService = context;
212         mVoiceRecognitionStarted = false;
213         mWaitingForVoiceRecognition = false;
214 
215         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
216         mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(
217                 PowerManager.PARTIAL_WAKE_LOCK, TAG + ":VoiceRecognition");
218         mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
219 
220         mDialingOut = false;
221         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
222         mPhonebook = new AtPhonebook(mService, this);
223         mPhoneState = new HeadsetPhoneState(context, this);
224         mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
225         mAdapter = BluetoothAdapter.getDefaultAdapter();
226         Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
227         intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
228         if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {
229             Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
230         }
231 
232         String max_hfp_clients = SystemProperties.get("bt.max.hfpclient.connections");
233         if (!max_hfp_clients.isEmpty() && (Integer.parseInt(max_hfp_clients) == 2)) {
234             max_hf_connections = Integer.parseInt(max_hfp_clients);
235         }
236         Log.d(TAG, "max_hf_connections = " + max_hf_connections);
237         Log.d(TAG,
238                 "in-band_ringing_support = " + BluetoothHeadset.isInbandRingingSupported(mService));
239         initializeNative(max_hf_connections, BluetoothHeadset.isInbandRingingSupported(mService));
240         mNativeAvailable = true;
241 
242         mDisconnected = new Disconnected();
243         mPending = new Pending();
244         mConnected = new Connected();
245         mAudioOn = new AudioOn();
246         // Multi HFP: initialise new class variable
247         mMultiHFPending = new MultiHFPending();
248 
249         if (sVoiceCommandIntent == null) {
250             sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
251             sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
252         }
253 
254         addState(mDisconnected);
255         addState(mPending);
256         addState(mConnected);
257         addState(mAudioOn);
258         // Multi HFP: add State
259         addState(mMultiHFPending);
260 
261         setInitialState(mDisconnected);
262     }
263 
make(HeadsetService context)264     static HeadsetStateMachine make(HeadsetService context) {
265         Log.d(TAG, "make");
266         HeadsetStateMachine hssm = new HeadsetStateMachine(context);
267         hssm.start();
268         return hssm;
269     }
270 
doQuit()271     public void doQuit() {
272         quitNow();
273     }
274 
cleanup()275     public void cleanup() {
276         if (mPhoneProxy != null) {
277             if (DBG) Log.d(TAG, "Unbinding service...");
278             synchronized (mConnection) {
279                 try {
280                     mPhoneProxy = null;
281                     mService.unbindService(mConnection);
282                 } catch (Exception re) {
283                     Log.e(TAG, "Error unbinding from IBluetoothHeadsetPhone", re);
284                 }
285             }
286         }
287         if (mPhoneState != null) {
288             mPhoneState.listenForPhoneState(false);
289             mPhoneState.cleanup();
290         }
291         if (mPhonebook != null) {
292             mPhonebook.cleanup();
293         }
294         if (mHeadsetAudioParam != null) {
295             mHeadsetAudioParam.clear();
296         }
297         if (mHeadsetBrsf != null) {
298             mHeadsetBrsf.clear();
299         }
300         if (mConnectedDevicesList != null) {
301             mConnectedDevicesList.clear();
302         }
303         if (mNativeAvailable) {
304             cleanupNative();
305             mNativeAvailable = false;
306         }
307     }
308 
dump(StringBuilder sb)309     public void dump(StringBuilder sb) {
310         ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
311         ProfileService.println(sb, "mTargetDevice: " + mTargetDevice);
312         ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice);
313         ProfileService.println(sb, "mActiveScoDevice: " + mActiveScoDevice);
314         ProfileService.println(sb, "mMultiDisconnectDevice: " + mMultiDisconnectDevice);
315         ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted);
316         ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
317         ProfileService.println(sb, "mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
318         ProfileService.println(sb, "StateMachine: " + this.toString());
319         ProfileService.println(sb, "mPhoneState: " + mPhoneState);
320         ProfileService.println(sb, "mAudioState: " + mAudioState);
321     }
322 
323     private class Disconnected extends State {
324         @Override
enter()325         public void enter() {
326             log("Enter Disconnected: " + getCurrentMessage().what + ", size: "
327                     + mConnectedDevicesList.size());
328             mPhonebook.resetAtState();
329             mPhoneState.listenForPhoneState(false);
330             mVoiceRecognitionStarted = false;
331             mWaitingForVoiceRecognition = false;
332             mSlcConnected = false;
333         }
334 
335         @Override
processMessage(Message message)336         public boolean processMessage(Message message) {
337             log("Disconnected process message: " + message.what + ", size: "
338                     + mConnectedDevicesList.size());
339             if (mConnectedDevicesList.size() != 0 || mTargetDevice != null
340                     || mIncomingDevice != null) {
341                 Log.e(TAG, "ERROR: mConnectedDevicesList is not empty,"
342                                 + "target, or mIncomingDevice not null in Disconnected");
343                 return NOT_HANDLED;
344             }
345 
346             boolean retValue = HANDLED;
347             switch (message.what) {
348                 case CONNECT:
349                     BluetoothDevice device = (BluetoothDevice) message.obj;
350                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
351                             BluetoothProfile.STATE_DISCONNECTED);
352 
353                     if (!connectHfpNative(getByteAddress(device))) {
354                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
355                                 BluetoothProfile.STATE_CONNECTING);
356                         break;
357                     }
358 
359                     synchronized (HeadsetStateMachine.this) {
360                         mTargetDevice = device;
361                         transitionTo(mPending);
362                     }
363                     // TODO(BT) remove CONNECT_TIMEOUT when the stack
364                     //          sends back events consistently
365                     Message m = obtainMessage(CONNECT_TIMEOUT);
366                     m.obj = device;
367                     sendMessageDelayed(m, 30000);
368                     break;
369                 case DISCONNECT:
370                     // ignore
371                     break;
372                 case INTENT_BATTERY_CHANGED:
373                     processIntentBatteryChanged((Intent) message.obj);
374                     break;
375                 case CALL_STATE_CHANGED:
376                     processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
377                     break;
378                 case STACK_EVENT:
379                     StackEvent event = (StackEvent) message.obj;
380                     if (DBG) {
381                         log("event type: " + event.type);
382                     }
383                     switch (event.type) {
384                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
385                             processConnectionEvent(event.valueInt, event.device);
386                             break;
387                         default:
388                             Log.e(TAG, "Unexpected stack event: " + event.type);
389                             break;
390                     }
391                     break;
392                 default:
393                     return NOT_HANDLED;
394             }
395             return retValue;
396         }
397 
398         @Override
exit()399         public void exit() {
400             log("Exit Disconnected: " + getCurrentMessage().what);
401         }
402 
403         // in Disconnected state
processConnectionEvent(int state, BluetoothDevice device)404         private void processConnectionEvent(int state, BluetoothDevice device) {
405             Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
406             switch (state) {
407                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
408                     Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device);
409                     break;
410                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
411                     if (okToConnect(device)) {
412                         Log.i(TAG, "Incoming Hf accepted");
413                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
414                                 BluetoothProfile.STATE_DISCONNECTED);
415                         synchronized (HeadsetStateMachine.this) {
416                             mIncomingDevice = device;
417                             transitionTo(mPending);
418                         }
419                     } else {
420                         Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
421                                         + " bondState=" + device.getBondState());
422                         // reject the connection and stay in Disconnected state itself
423                         disconnectHfpNative(getByteAddress(device));
424                         // the other profile connection should be initiated
425                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
426                                 BluetoothProfile.STATE_DISCONNECTED);
427                     }
428                     break;
429                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
430                     Log.w(TAG, "HFP Connected from Disconnected state");
431                     if (okToConnect(device)) {
432                         Log.i(TAG, "Incoming Hf accepted");
433                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
434                                 BluetoothProfile.STATE_DISCONNECTED);
435                         synchronized (HeadsetStateMachine.this) {
436                             if (!mConnectedDevicesList.contains(device)) {
437                                 mConnectedDevicesList.add(device);
438                                 Log.d(TAG, "device " + device.getAddress()
439                                                 + " is adding in Disconnected state");
440                             }
441                             mCurrentDevice = device;
442                             transitionTo(mConnected);
443                         }
444                         configAudioParameters(device);
445                     } else {
446                         // reject the connection and stay in Disconnected state itself
447                         Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
448                                         + " bondState=" + device.getBondState());
449                         disconnectHfpNative(getByteAddress(device));
450                         // the other profile connection should be initiated
451                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
452                                 BluetoothProfile.STATE_DISCONNECTED);
453                     }
454                     break;
455                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
456                     Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device);
457                     break;
458                 default:
459                     Log.e(TAG, "Incorrect state: " + state);
460                     break;
461             }
462         }
463     }
464 
465     private class Pending extends State {
466         @Override
enter()467         public void enter() {
468             log("Enter Pending: " + getCurrentMessage().what);
469         }
470 
471         @Override
processMessage(Message message)472         public boolean processMessage(Message message) {
473             log("Pending process message: " + message.what + ", size: "
474                     + mConnectedDevicesList.size());
475 
476             boolean retValue = HANDLED;
477             switch (message.what) {
478                 case CONNECT:
479                 case CONNECT_AUDIO:
480                     deferMessage(message);
481                     break;
482                 case CONNECT_TIMEOUT:
483                     onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
484                             getByteAddress(mTargetDevice));
485                     break;
486                 case DISCONNECT:
487                     BluetoothDevice device = (BluetoothDevice) message.obj;
488                     if (mCurrentDevice != null && mTargetDevice != null
489                             && mTargetDevice.equals(device)) {
490                         // cancel connection to the mTargetDevice
491                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
492                                 BluetoothProfile.STATE_CONNECTING);
493                         synchronized (HeadsetStateMachine.this) {
494                             mTargetDevice = null;
495                         }
496                     } else {
497                         deferMessage(message);
498                     }
499                     break;
500                 case INTENT_BATTERY_CHANGED:
501                     processIntentBatteryChanged((Intent) message.obj);
502                     break;
503                 case CALL_STATE_CHANGED:
504                     processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
505                     break;
506                 case STACK_EVENT:
507                     StackEvent event = (StackEvent) message.obj;
508                     if (DBG) {
509                         log("event type: " + event.type);
510                     }
511                     switch (event.type) {
512                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
513                             BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
514                             if (device1 != null && device1.equals(event.device)) {
515                                 Log.d(TAG, "remove connect timeout for device = " + device1);
516                                 removeMessages(CONNECT_TIMEOUT);
517                             }
518                             processConnectionEvent(event.valueInt, event.device);
519                             break;
520 
521                         case EVENT_TYPE_BIND:
522                             processAtBind(event.valueString, event.device);
523                             break;
524 
525                         case EVENT_TYPE_BIEV:
526                             processAtBiev(event.valueInt, event.valueInt2, event.device);
527                             break;
528 
529                         default:
530                             Log.e(TAG, "Unexpected event: " + event.type);
531                             break;
532                     }
533                     break;
534                 default:
535                     return NOT_HANDLED;
536             }
537             return retValue;
538         }
539 
540         // in Pending state
processConnectionEvent(int state, BluetoothDevice device)541         private void processConnectionEvent(int state, BluetoothDevice device) {
542             Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
543             switch (state) {
544                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
545                     if (mConnectedDevicesList.contains(device)) {
546                         synchronized (HeadsetStateMachine.this) {
547                             mConnectedDevicesList.remove(device);
548                             mHeadsetAudioParam.remove(device);
549                             mHeadsetBrsf.remove(device);
550                             Log.d(TAG, "device " + device.getAddress()
551                                             + " is removed in Pending state");
552                         }
553 
554                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
555                                 BluetoothProfile.STATE_DISCONNECTING);
556                         synchronized (HeadsetStateMachine.this) {
557                             mCurrentDevice = null;
558                         }
559 
560                         processWBSEvent(0, device); /* disable WBS audio parameters */
561 
562                         if (mTargetDevice != null) {
563                             if (!connectHfpNative(getByteAddress(mTargetDevice))) {
564                                 broadcastConnectionState(mTargetDevice,
565                                         BluetoothProfile.STATE_DISCONNECTED,
566                                         BluetoothProfile.STATE_CONNECTING);
567                                 synchronized (HeadsetStateMachine.this) {
568                                     mTargetDevice = null;
569                                     transitionTo(mDisconnected);
570                                 }
571                             }
572                         } else {
573                             synchronized (HeadsetStateMachine.this) {
574                                 mIncomingDevice = null;
575                                 if (mConnectedDevicesList.size() == 0) {
576                                     transitionTo(mDisconnected);
577                                 } else {
578                                     processMultiHFConnected(device);
579                                 }
580                             }
581                         }
582                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
583                         // outgoing connection failed
584                         broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
585                                 BluetoothProfile.STATE_CONNECTING);
586                         synchronized (HeadsetStateMachine.this) {
587                             mTargetDevice = null;
588                             if (mConnectedDevicesList.size() == 0) {
589                                 transitionTo(mDisconnected);
590                             } else {
591                                 transitionTo(mConnected);
592                             }
593                         }
594                     } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
595                         broadcastConnectionState(mIncomingDevice,
596                                 BluetoothProfile.STATE_DISCONNECTED,
597                                 BluetoothProfile.STATE_CONNECTING);
598                         synchronized (HeadsetStateMachine.this) {
599                             mIncomingDevice = null;
600                             if (mConnectedDevicesList.size() == 0) {
601                                 transitionTo(mDisconnected);
602                             } else {
603                                 transitionTo(mConnected);
604                             }
605                         }
606                     } else {
607                         Log.e(TAG, "Unknown device Disconnected: " + device);
608                     }
609                     break;
610                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
611                     if (mConnectedDevicesList.contains(device)) {
612                         // disconnection failed
613                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
614                                 BluetoothProfile.STATE_DISCONNECTING);
615                         if (mTargetDevice != null) {
616                             broadcastConnectionState(mTargetDevice,
617                                     BluetoothProfile.STATE_DISCONNECTED,
618                                     BluetoothProfile.STATE_CONNECTING);
619                         }
620                         synchronized (HeadsetStateMachine.this) {
621                             mTargetDevice = null;
622                             transitionTo(mConnected);
623                         }
624                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
625                         synchronized (HeadsetStateMachine.this) {
626                             mCurrentDevice = device;
627                             mConnectedDevicesList.add(device);
628                             Log.d(TAG,
629                                     "device " + device.getAddress() + " is added in Pending state");
630                             mTargetDevice = null;
631                             transitionTo(mConnected);
632                         }
633                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
634                                 BluetoothProfile.STATE_CONNECTING);
635                         configAudioParameters(device);
636                     } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
637                         synchronized (HeadsetStateMachine.this) {
638                             mCurrentDevice = device;
639                             mConnectedDevicesList.add(device);
640                             Log.d(TAG,
641                                     "device " + device.getAddress() + " is added in Pending state");
642                             mIncomingDevice = null;
643                             transitionTo(mConnected);
644                         }
645                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
646                                 BluetoothProfile.STATE_CONNECTING);
647                         configAudioParameters(device);
648                     } else {
649                         Log.w(TAG, "Some other incoming HF connected in Pending state");
650                         if (okToConnect(device)) {
651                             Log.i(TAG, "Incoming Hf accepted");
652                             broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
653                                     BluetoothProfile.STATE_DISCONNECTED);
654                             synchronized (HeadsetStateMachine.this) {
655                                 mCurrentDevice = device;
656                                 mConnectedDevicesList.add(device);
657                                 Log.d(TAG, "device " + device.getAddress()
658                                                 + " is added in Pending state");
659                             }
660                             configAudioParameters(device);
661                         } else {
662                             // reject the connection and stay in Pending state itself
663                             Log.i(TAG, "Incoming Hf rejected. priority="
664                                             + mService.getPriority(device) + " bondState="
665                                             + device.getBondState());
666                             disconnectHfpNative(getByteAddress(device));
667                         }
668                     }
669                     break;
670                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
671                     if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
672                         log("current device tries to connect back");
673                         // TODO(BT) ignore or reject
674                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
675                         // The stack is connecting to target device or
676                         // there is an incoming connection from the target device at the same time
677                         // we already broadcasted the intent, doing nothing here
678                         if (DBG) {
679                             log("Stack and target device are connecting");
680                         }
681                     } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
682                         Log.e(TAG, "Another connecting event on the incoming device");
683                     } else {
684                         // We get an incoming connecting request while Pending
685                         // TODO(BT) is stack handing this case? let's ignore it for now
686                         log("Incoming connection while pending, ignore");
687                     }
688                     break;
689                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
690                     if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
691                         // we already broadcasted the intent, doing nothing here
692                         if (DBG) {
693                             log("stack is disconnecting mCurrentDevice");
694                         }
695                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
696                         Log.e(TAG, "TargetDevice is getting disconnected");
697                     } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
698                         Log.e(TAG, "IncomingDevice is getting disconnected");
699                     } else {
700                         Log.e(TAG, "Disconnecting unknow device: " + device);
701                     }
702                     break;
703                 default:
704                     Log.e(TAG, "Incorrect state: " + state);
705                     break;
706             }
707         }
708 
processMultiHFConnected(BluetoothDevice device)709         private void processMultiHFConnected(BluetoothDevice device) {
710             log("Pending state: processMultiHFConnected");
711             /* Assign the current activedevice again if the disconnected
712                          device equals to the current active device*/
713             if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
714                 transitionTo(mConnected);
715                 int deviceSize = mConnectedDevicesList.size();
716                 mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1);
717             } else {
718                 // The disconnected device is not current active device
719                 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
720                     transitionTo(mAudioOn);
721                 else
722                     transitionTo(mConnected);
723             }
724             log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice);
725             log("Pending state: processMultiHFConnected ,"
726                     + "fake broadcasting for mCurrentDevice");
727             broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
728                     BluetoothProfile.STATE_DISCONNECTED);
729         }
730     }
731 
732     private class Connected extends State {
733         @Override
enter()734         public void enter() {
735             // Remove pending connection attempts that were deferred during the pending
736             // state. This is to prevent auto connect attempts from disconnecting
737             // devices that previously successfully connected.
738             // TODO: This needs to check for multiple HFP connections, once supported...
739             removeDeferredMessages(CONNECT);
740 
741             log("Enter Connected: " + getCurrentMessage().what + ", size: "
742                     + mConnectedDevicesList.size());
743             // start phone state listener here so that the CIND response as part of SLC can be
744             // responded to, correctly.
745             // we may enter Connected from Disconnected/Pending/AudioOn. listenForPhoneState
746             // internally handles multiple calls to start listen
747             mPhoneState.listenForPhoneState(true);
748         }
749 
750         @Override
processMessage(Message message)751         public boolean processMessage(Message message) {
752             log("Connected process message: " + message.what + ", size: "
753                     + mConnectedDevicesList.size());
754             if (DBG) {
755                 if (mConnectedDevicesList.size() == 0) {
756                     log("ERROR: mConnectedDevicesList is empty in Connected");
757                     return NOT_HANDLED;
758                 }
759             }
760 
761             boolean retValue = HANDLED;
762             switch (message.what) {
763                 case CONNECT: {
764                     BluetoothDevice device = (BluetoothDevice) message.obj;
765                     if (device == null) {
766                         break;
767                     }
768 
769                     if (mConnectedDevicesList.contains(device)) {
770                         Log.e(TAG, "ERROR: Connect received for already connected device, Ignore");
771                         break;
772                     }
773 
774                     if (mConnectedDevicesList.size() >= max_hf_connections) {
775                         BluetoothDevice DisconnectConnectedDevice = null;
776                         IState CurrentAudioState = getCurrentState();
777                         Log.d(TAG, "Reach to max size, disconnect one of them first");
778                         /* TODO: Disconnect based on CoD */
779                         DisconnectConnectedDevice = mConnectedDevicesList.get(0);
780 
781                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
782                                 BluetoothProfile.STATE_DISCONNECTED);
783 
784                         if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) {
785                             broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
786                                     BluetoothProfile.STATE_CONNECTING);
787                             break;
788                         } else {
789                             broadcastConnectionState(DisconnectConnectedDevice,
790                                     BluetoothProfile.STATE_DISCONNECTING,
791                                     BluetoothProfile.STATE_CONNECTED);
792                         }
793 
794                         synchronized (HeadsetStateMachine.this) {
795                             mTargetDevice = device;
796                             if (max_hf_connections == 1) {
797                                 transitionTo(mPending);
798                             } else {
799                                 mMultiDisconnectDevice = DisconnectConnectedDevice;
800                                 transitionTo(mMultiHFPending);
801                             }
802                             DisconnectConnectedDevice = null;
803                         }
804                     } else if (mConnectedDevicesList.size() < max_hf_connections) {
805                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
806                                 BluetoothProfile.STATE_DISCONNECTED);
807                         if (!connectHfpNative(getByteAddress(device))) {
808                             broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
809                                     BluetoothProfile.STATE_CONNECTING);
810                             break;
811                         }
812                         synchronized (HeadsetStateMachine.this) {
813                             mTargetDevice = device;
814                             // Transtion to MultiHFPending state for Multi HF connection
815                             transitionTo(mMultiHFPending);
816                         }
817                     }
818                     Message m = obtainMessage(CONNECT_TIMEOUT);
819                     m.obj = device;
820                     sendMessageDelayed(m, 30000);
821                 } break;
822                 case DISCONNECT: {
823                     BluetoothDevice device = (BluetoothDevice) message.obj;
824                     if (!mConnectedDevicesList.contains(device)) {
825                         break;
826                     }
827                     broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
828                             BluetoothProfile.STATE_CONNECTED);
829                     if (!disconnectHfpNative(getByteAddress(device))) {
830                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
831                                 BluetoothProfile.STATE_DISCONNECTED);
832                         break;
833                     }
834 
835                     if (mConnectedDevicesList.size() > 1) {
836                         mMultiDisconnectDevice = device;
837                         transitionTo(mMultiHFPending);
838                     } else {
839                         transitionTo(mPending);
840                     }
841                 } break;
842                 case CONNECT_AUDIO: {
843                     BluetoothDevice device = mCurrentDevice;
844                     if (!isScoAcceptable()) {
845                         Log.w(TAG,
846                                 "No Active/Held call, no call setup, and no in-band ringing, not allowing SCO");
847                         break;
848                     }
849                     // TODO(BT) when failure, broadcast audio connecting to disconnected intent
850                     //          check if device matches mCurrentDevice
851                     if (mActiveScoDevice != null) {
852                         log("connectAudioNative in Connected; mActiveScoDevice is not null");
853                         device = mActiveScoDevice;
854                     }
855                     log("connectAudioNative in Connected for device = " + device);
856                     connectAudioNative(getByteAddress(device));
857                 } break;
858                 case VOICE_RECOGNITION_START:
859                     processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
860                     break;
861                 case VOICE_RECOGNITION_STOP:
862                     processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
863                     break;
864                 case CALL_STATE_CHANGED:
865                     processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
866                     break;
867                 case INTENT_BATTERY_CHANGED:
868                     processIntentBatteryChanged((Intent) message.obj);
869                     break;
870                 case DEVICE_STATE_CHANGED:
871                     processDeviceStateChanged((HeadsetDeviceState) message.obj);
872                     break;
873                 case SEND_CCLC_RESPONSE:
874                     processSendClccResponse((HeadsetClccResponse) message.obj);
875                     break;
876                 case CLCC_RSP_TIMEOUT: {
877                     BluetoothDevice device = (BluetoothDevice) message.obj;
878                     clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
879                 } break;
880                 case SEND_VENDOR_SPECIFIC_RESULT_CODE:
881                     processSendVendorSpecificResultCode(
882                             (HeadsetVendorSpecificResultCode) message.obj);
883                     break;
884                 case DIALING_OUT_TIMEOUT: {
885                     BluetoothDevice device = (BluetoothDevice) message.obj;
886                     if (mDialingOut) {
887                         mDialingOut = false;
888                         atResponseCodeNative(
889                                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
890                     }
891                 } break;
892                 case VIRTUAL_CALL_START:
893                     initiateScoUsingVirtualVoiceCall();
894                     break;
895                 case VIRTUAL_CALL_STOP:
896                     terminateScoUsingVirtualVoiceCall();
897                     break;
898                 case ENABLE_WBS: {
899                     BluetoothDevice device = (BluetoothDevice) message.obj;
900                     configureWBSNative(getByteAddress(device), WBS_CODEC);
901                     break;
902                 }
903                 case DISABLE_WBS: {
904                     BluetoothDevice device = (BluetoothDevice) message.obj;
905                     configureWBSNative(getByteAddress(device), NBS_CODEC);
906                     break;
907                 }
908                 case BIND_RESPONSE: {
909                     BluetoothDevice device = (BluetoothDevice) message.obj;
910                     bindResponseNative(
911                             (int) message.arg1, message.arg2 == 1, getByteAddress(device));
912                 } break;
913                 case START_VR_TIMEOUT: {
914                     BluetoothDevice device = (BluetoothDevice) message.obj;
915                     if (mWaitingForVoiceRecognition) {
916                         device = (BluetoothDevice) message.obj;
917                         mWaitingForVoiceRecognition = false;
918                         Log.e(TAG, "Timeout waiting for voice recognition to start");
919                         atResponseCodeNative(
920                                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
921                     }
922                 } break;
923                 case STACK_EVENT:
924                     StackEvent event = (StackEvent) message.obj;
925                     if (DBG) {
926                         log("event type: " + event.type + "event device : " + event.device);
927                     }
928                     switch (event.type) {
929                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
930                             processConnectionEvent(event.valueInt, event.device);
931                             break;
932                         case EVENT_TYPE_AUDIO_STATE_CHANGED:
933                             processAudioEvent(event.valueInt, event.device);
934                             break;
935                         case EVENT_TYPE_VR_STATE_CHANGED:
936                             processVrEvent(event.valueInt, event.device);
937                             break;
938                         case EVENT_TYPE_ANSWER_CALL:
939                             // TODO(BT) could answer call happen on Connected state?
940                             processAnswerCall(event.device);
941                             break;
942                         case EVENT_TYPE_HANGUP_CALL:
943                             // TODO(BT) could hangup call happen on Connected state?
944                             processHangupCall(event.device);
945                             break;
946                         case EVENT_TYPE_VOLUME_CHANGED:
947                             processVolumeEvent(event.valueInt, event.valueInt2, event.device);
948                             break;
949                         case EVENT_TYPE_DIAL_CALL:
950                             processDialCall(event.valueString, event.device);
951                             break;
952                         case EVENT_TYPE_SEND_DTMF:
953                             processSendDtmf(event.valueInt, event.device);
954                             break;
955                         case EVENT_TYPE_NOICE_REDUCTION:
956                             processNoiceReductionEvent(event.valueInt, event.device);
957                             break;
958                         case EVENT_TYPE_WBS:
959                             Log.d(TAG, "EVENT_TYPE_WBS codec is " + event.valueInt);
960                             processWBSEvent(event.valueInt, event.device);
961                             break;
962                         case EVENT_TYPE_AT_CHLD:
963                             processAtChld(event.valueInt, event.device);
964                             break;
965                         case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
966                             processSubscriberNumberRequest(event.device);
967                             break;
968                         case EVENT_TYPE_AT_CIND:
969                             processAtCind(event.device);
970                             break;
971                         case EVENT_TYPE_AT_COPS:
972                             processAtCops(event.device);
973                             break;
974                         case EVENT_TYPE_AT_CLCC:
975                             processAtClcc(event.device);
976                             break;
977                         case EVENT_TYPE_UNKNOWN_AT:
978                             processUnknownAt(event.valueString, event.device);
979                             break;
980                         case EVENT_TYPE_KEY_PRESSED:
981                             processKeyPressed(event.device);
982                             break;
983                         case EVENT_TYPE_BIND:
984                             processAtBind(event.valueString, event.device);
985                             break;
986                         case EVENT_TYPE_BIEV:
987                             processAtBiev(event.valueInt, event.valueInt2, event.device);
988                             break;
989                         default:
990                             Log.e(TAG, "Unknown stack event: " + event.type);
991                             break;
992                     }
993                     break;
994                 default:
995                     return NOT_HANDLED;
996             }
997             return retValue;
998         }
999 
1000         // in Connected state
processConnectionEvent(int state, BluetoothDevice device)1001         private void processConnectionEvent(int state, BluetoothDevice device) {
1002             Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
1003             switch (state) {
1004                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1005                     if (mConnectedDevicesList.contains(device)) {
1006                         processWBSEvent(0, device); /* disable WBS audio parameters */
1007                         synchronized (HeadsetStateMachine.this) {
1008                             mConnectedDevicesList.remove(device);
1009                             mHeadsetAudioParam.remove(device);
1010                             mHeadsetBrsf.remove(device);
1011                             Log.d(TAG, "device " + device.getAddress()
1012                                             + " is removed in Connected state");
1013 
1014                             if (mConnectedDevicesList.size() == 0) {
1015                                 mCurrentDevice = null;
1016                                 transitionTo(mDisconnected);
1017                             } else {
1018                                 processMultiHFConnected(device);
1019                             }
1020                         }
1021                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1022                                 BluetoothProfile.STATE_CONNECTED);
1023                     } else {
1024                         Log.e(TAG, "Disconnected from unknown device: " + device);
1025                     }
1026                     break;
1027                 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1028                     processSlcConnected();
1029                     break;
1030                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1031                     if (mConnectedDevicesList.contains(device)) {
1032                         mIncomingDevice = null;
1033                         mTargetDevice = null;
1034                         break;
1035                     }
1036                     Log.w(TAG, "HFP to be Connected in Connected state");
1037                     if (okToConnect(device)
1038                             && (mConnectedDevicesList.size() < max_hf_connections)) {
1039                         Log.i(TAG, "Incoming Hf accepted");
1040                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1041                                 BluetoothProfile.STATE_DISCONNECTED);
1042                         synchronized (HeadsetStateMachine.this) {
1043                             if (!mConnectedDevicesList.contains(device)) {
1044                                 mCurrentDevice = device;
1045                                 mConnectedDevicesList.add(device);
1046                                 Log.d(TAG, "device " + device.getAddress()
1047                                                 + " is added in Connected state");
1048                             }
1049                             transitionTo(mConnected);
1050                         }
1051                         configAudioParameters(device);
1052                     } else {
1053                         // reject the connection and stay in Connected state itself
1054                         Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
1055                                         + " bondState=" + device.getBondState());
1056                         disconnectHfpNative(getByteAddress(device));
1057                     }
1058                     break;
1059                 default:
1060                     Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1061                     break;
1062             }
1063         }
1064 
1065         // in Connected state
processAudioEvent(int state, BluetoothDevice device)1066         private void processAudioEvent(int state, BluetoothDevice device) {
1067             if (!mConnectedDevicesList.contains(device)) {
1068                 Log.e(TAG, "Audio changed on disconnected device: " + device);
1069                 return;
1070             }
1071 
1072             switch (state) {
1073                 case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1074                     if (!isScoAcceptable()) {
1075                         Log.e(TAG, "Audio Connected without any listener");
1076                         disconnectAudioNative(getByteAddress(device));
1077                         break;
1078                     }
1079 
1080                     // TODO(BT) should I save the state for next broadcast as the prevState?
1081                     mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
1082                     setAudioParameters(device); /*Set proper Audio Paramters.*/
1083                     mAudioManager.setBluetoothScoOn(true);
1084                     broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1085                             BluetoothHeadset.STATE_AUDIO_CONNECTING);
1086                     mActiveScoDevice = device;
1087                     transitionTo(mAudioOn);
1088                     break;
1089                 case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1090                     mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
1091                     broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
1092                             BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1093                     break;
1094                 // TODO(BT) process other states
1095                 default:
1096                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1097                     break;
1098             }
1099         }
1100 
processSlcConnected()1101         private void processSlcConnected() {
1102             mSlcConnected = true;
1103             if (mPhoneProxy != null) {
1104                 try {
1105                     mPhoneProxy.queryPhoneState();
1106                 } catch (RemoteException e) {
1107                     Log.e(TAG, Log.getStackTraceString(new Throwable()));
1108                 }
1109             } else {
1110                 Log.e(TAG, "Handsfree phone proxy null for query phone state");
1111             }
1112         }
1113 
processMultiHFConnected(BluetoothDevice device)1114         private void processMultiHFConnected(BluetoothDevice device) {
1115             log("Connect state: processMultiHFConnected");
1116             if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1117                 log("mActiveScoDevice is disconnected, setting it to null");
1118                 mActiveScoDevice = null;
1119             }
1120             /* Assign the current activedevice again if the disconnected
1121                          device equals to the current active device */
1122             if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1123                 transitionTo(mConnected);
1124                 int deviceSize = mConnectedDevicesList.size();
1125                 mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1);
1126             } else {
1127                 // The disconnected device is not current active device
1128                 transitionTo(mConnected);
1129             }
1130             log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice);
1131             log("Connect state: processMultiHFConnected ,"
1132                     + "fake broadcasting for mCurrentDevice");
1133             broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1134                     BluetoothProfile.STATE_DISCONNECTED);
1135         }
1136     }
1137 
1138     private class AudioOn extends State {
1139         @Override
enter()1140         public void enter() {
1141             log("Enter AudioOn: " + getCurrentMessage().what + ", size: "
1142                     + mConnectedDevicesList.size());
1143         }
1144 
1145         @Override
processMessage(Message message)1146         public boolean processMessage(Message message) {
1147             log("AudioOn process message: " + message.what + ", size: "
1148                     + mConnectedDevicesList.size());
1149             if (DBG) {
1150                 if (mConnectedDevicesList.size() == 0) {
1151                     log("ERROR: mConnectedDevicesList is empty in AudioOn");
1152                     return NOT_HANDLED;
1153                 }
1154             }
1155 
1156             boolean retValue = HANDLED;
1157             switch (message.what) {
1158                 case CONNECT: {
1159                     BluetoothDevice device = (BluetoothDevice) message.obj;
1160                     if (device == null) {
1161                         break;
1162                     }
1163 
1164                     if (mConnectedDevicesList.contains(device)) {
1165                         break;
1166                     }
1167 
1168                     if (max_hf_connections == 1) {
1169                         deferMessage(obtainMessage(DISCONNECT, mCurrentDevice));
1170                         deferMessage(obtainMessage(CONNECT, device));
1171                         if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {
1172                             Log.d(TAG, "Disconnecting SCO audio for device = " + mCurrentDevice);
1173                         } else {
1174                             Log.e(TAG, "disconnectAudioNative failed");
1175                         }
1176                         break;
1177                     }
1178 
1179                     if (mConnectedDevicesList.size() >= max_hf_connections) {
1180                         BluetoothDevice DisconnectConnectedDevice = null;
1181                         IState CurrentAudioState = getCurrentState();
1182                         Log.d(TAG, "Reach to max size, disconnect "
1183                                         + "one of them first");
1184                         DisconnectConnectedDevice = mConnectedDevicesList.get(0);
1185 
1186                         if (mActiveScoDevice.equals(DisconnectConnectedDevice)) {
1187                             DisconnectConnectedDevice = mConnectedDevicesList.get(1);
1188                         }
1189 
1190                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1191                                 BluetoothProfile.STATE_DISCONNECTED);
1192 
1193                         if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) {
1194                             broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1195                                     BluetoothProfile.STATE_CONNECTING);
1196                             break;
1197                         } else {
1198                             broadcastConnectionState(DisconnectConnectedDevice,
1199                                     BluetoothProfile.STATE_DISCONNECTING,
1200                                     BluetoothProfile.STATE_CONNECTED);
1201                         }
1202 
1203                         synchronized (HeadsetStateMachine.this) {
1204                             mTargetDevice = device;
1205                             mMultiDisconnectDevice = DisconnectConnectedDevice;
1206                             transitionTo(mMultiHFPending);
1207                             DisconnectConnectedDevice = null;
1208                         }
1209                     } else if (mConnectedDevicesList.size() < max_hf_connections) {
1210                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1211                                 BluetoothProfile.STATE_DISCONNECTED);
1212                         if (!connectHfpNative(getByteAddress(device))) {
1213                             broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1214                                     BluetoothProfile.STATE_CONNECTING);
1215                             break;
1216                         }
1217                         synchronized (HeadsetStateMachine.this) {
1218                             mTargetDevice = device;
1219                             // Transtion to MultilHFPending state for Multi handsfree connection
1220                             transitionTo(mMultiHFPending);
1221                         }
1222                     }
1223                     Message m = obtainMessage(CONNECT_TIMEOUT);
1224                     m.obj = device;
1225                     sendMessageDelayed(m, 30000);
1226                 } break;
1227                 case CONNECT_TIMEOUT:
1228                     onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
1229                             getByteAddress(mTargetDevice));
1230                     break;
1231                 case DISCONNECT: {
1232                     BluetoothDevice device = (BluetoothDevice) message.obj;
1233                     if (!mConnectedDevicesList.contains(device)) {
1234                         break;
1235                     }
1236                     if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1237                         // The disconnected device is active SCO device
1238                         Log.d(TAG, "AudioOn, the disconnected device"
1239                                         + "is active SCO device");
1240                         deferMessage(obtainMessage(DISCONNECT, message.obj));
1241                         // Disconnect BT SCO first
1242                         if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1243                             log("Disconnecting SCO audio");
1244                         } else {
1245                             // if disconnect BT SCO failed, transition to mConnected state
1246                             transitionTo(mConnected);
1247                         }
1248                     } else {
1249                         /* Do not disconnect BT SCO if the disconnected
1250                            device is not active SCO device */
1251                         Log.d(TAG, "AudioOn, the disconnected device"
1252                                         + "is not active SCO device");
1253                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
1254                                 BluetoothProfile.STATE_CONNECTED);
1255                         // Should be still in AudioOn state
1256                         if (!disconnectHfpNative(getByteAddress(device))) {
1257                             Log.w(TAG, "AudioOn, disconnect device failed");
1258                             broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1259                                     BluetoothProfile.STATE_DISCONNECTING);
1260                             break;
1261                         }
1262                         /* Transtion to MultiHFPending state for Multi
1263                            handsfree connection */
1264                         if (mConnectedDevicesList.size() > 1) {
1265                             mMultiDisconnectDevice = device;
1266                             transitionTo(mMultiHFPending);
1267                         }
1268                     }
1269                 } break;
1270                 case DISCONNECT_AUDIO:
1271                     if (mActiveScoDevice != null) {
1272                         if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1273                             log("Disconnecting SCO audio for device = " + mActiveScoDevice);
1274                         } else {
1275                             Log.e(TAG, "disconnectAudioNative failed"
1276                                             + "for device = " + mActiveScoDevice);
1277                         }
1278                     }
1279                     break;
1280                 case VOICE_RECOGNITION_START:
1281                     processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
1282                     break;
1283                 case VOICE_RECOGNITION_STOP:
1284                     processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
1285                     break;
1286                 case INTENT_SCO_VOLUME_CHANGED:
1287                     if (mActiveScoDevice != null) {
1288                         processIntentScoVolume((Intent) message.obj, mActiveScoDevice);
1289                     }
1290                     break;
1291                 case CALL_STATE_CHANGED:
1292                     processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
1293                     break;
1294                 case INTENT_BATTERY_CHANGED:
1295                     processIntentBatteryChanged((Intent) message.obj);
1296                     break;
1297                 case DEVICE_STATE_CHANGED:
1298                     processDeviceStateChanged((HeadsetDeviceState) message.obj);
1299                     break;
1300                 case SEND_CCLC_RESPONSE:
1301                     processSendClccResponse((HeadsetClccResponse) message.obj);
1302                     break;
1303                 case CLCC_RSP_TIMEOUT: {
1304                     BluetoothDevice device = (BluetoothDevice) message.obj;
1305                     clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
1306                     break;
1307                 }
1308                 case SEND_VENDOR_SPECIFIC_RESULT_CODE:
1309                     processSendVendorSpecificResultCode(
1310                             (HeadsetVendorSpecificResultCode) message.obj);
1311                     break;
1312 
1313                 case VIRTUAL_CALL_START:
1314                     initiateScoUsingVirtualVoiceCall();
1315                     break;
1316                 case VIRTUAL_CALL_STOP:
1317                     terminateScoUsingVirtualVoiceCall();
1318                     break;
1319 
1320                 case DIALING_OUT_TIMEOUT: {
1321                     if (mDialingOut) {
1322                         BluetoothDevice device = (BluetoothDevice) message.obj;
1323                         mDialingOut = false;
1324                         atResponseCodeNative(
1325                                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
1326                     }
1327                     break;
1328                 }
1329                 case START_VR_TIMEOUT: {
1330                     if (mWaitingForVoiceRecognition) {
1331                         BluetoothDevice device = (BluetoothDevice) message.obj;
1332                         mWaitingForVoiceRecognition = false;
1333                         Log.e(TAG, "Timeout waiting for voice recognition"
1334                                         + "to start");
1335                         atResponseCodeNative(
1336                                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
1337                     }
1338                     break;
1339                 }
1340                 case STACK_EVENT:
1341                     StackEvent event = (StackEvent) message.obj;
1342                     if (DBG) {
1343                         log("event type: " + event.type);
1344                     }
1345                     switch (event.type) {
1346                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1347                             BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
1348                             if (device1 != null && device1.equals(event.device)) {
1349                                 Log.d(TAG, "remove connect timeout for device = " + device1);
1350                                 removeMessages(CONNECT_TIMEOUT);
1351                             }
1352                             processConnectionEvent(event.valueInt, event.device);
1353                             break;
1354                         case EVENT_TYPE_AUDIO_STATE_CHANGED:
1355                             processAudioEvent(event.valueInt, event.device);
1356                             break;
1357                         case EVENT_TYPE_VR_STATE_CHANGED:
1358                             processVrEvent(event.valueInt, event.device);
1359                             break;
1360                         case EVENT_TYPE_ANSWER_CALL:
1361                             processAnswerCall(event.device);
1362                             break;
1363                         case EVENT_TYPE_HANGUP_CALL:
1364                             processHangupCall(event.device);
1365                             break;
1366                         case EVENT_TYPE_VOLUME_CHANGED:
1367                             processVolumeEvent(event.valueInt, event.valueInt2, event.device);
1368                             break;
1369                         case EVENT_TYPE_DIAL_CALL:
1370                             processDialCall(event.valueString, event.device);
1371                             break;
1372                         case EVENT_TYPE_SEND_DTMF:
1373                             processSendDtmf(event.valueInt, event.device);
1374                             break;
1375                         case EVENT_TYPE_NOICE_REDUCTION:
1376                             processNoiceReductionEvent(event.valueInt, event.device);
1377                             break;
1378                         case EVENT_TYPE_AT_CHLD:
1379                             processAtChld(event.valueInt, event.device);
1380                             break;
1381                         case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1382                             processSubscriberNumberRequest(event.device);
1383                             break;
1384                         case EVENT_TYPE_AT_CIND:
1385                             processAtCind(event.device);
1386                             break;
1387                         case EVENT_TYPE_AT_COPS:
1388                             processAtCops(event.device);
1389                             break;
1390                         case EVENT_TYPE_AT_CLCC:
1391                             processAtClcc(event.device);
1392                             break;
1393                         case EVENT_TYPE_UNKNOWN_AT:
1394                             processUnknownAt(event.valueString, event.device);
1395                             break;
1396                         case EVENT_TYPE_KEY_PRESSED:
1397                             processKeyPressed(event.device);
1398                             break;
1399                         case EVENT_TYPE_BIND:
1400                             processAtBind(event.valueString, event.device);
1401                             break;
1402                         case EVENT_TYPE_BIEV:
1403                             processAtBiev(event.valueInt, event.valueInt2, event.device);
1404                             break;
1405                         default:
1406                             Log.e(TAG, "Unknown stack event: " + event.type);
1407                             break;
1408                     }
1409                     break;
1410                 default:
1411                     return NOT_HANDLED;
1412             }
1413             return retValue;
1414         }
1415 
1416         // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this
processConnectionEvent(int state, BluetoothDevice device)1417         private void processConnectionEvent(int state, BluetoothDevice device) {
1418             Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
1419             switch (state) {
1420                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1421                     if (mConnectedDevicesList.contains(device)) {
1422                         if (mActiveScoDevice != null && mActiveScoDevice.equals(device)
1423                                 && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1424                             processAudioEvent(HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device);
1425                         }
1426 
1427                         synchronized (HeadsetStateMachine.this) {
1428                             mConnectedDevicesList.remove(device);
1429                             mHeadsetAudioParam.remove(device);
1430                             mHeadsetBrsf.remove(device);
1431                             Log.d(TAG, "device " + device.getAddress()
1432                                             + " is removed in AudioOn state");
1433                             broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1434                                     BluetoothProfile.STATE_CONNECTED);
1435                             processWBSEvent(0, device); /* disable WBS audio parameters */
1436                             if (mConnectedDevicesList.size() == 0) {
1437                                 transitionTo(mDisconnected);
1438                             } else {
1439                                 processMultiHFConnected(device);
1440                             }
1441                         }
1442                     } else {
1443                         Log.e(TAG, "Disconnected from unknown device: " + device);
1444                     }
1445                     break;
1446                 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1447                     processSlcConnected();
1448                     break;
1449                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1450                     if (mConnectedDevicesList.contains(device)) {
1451                         mIncomingDevice = null;
1452                         mTargetDevice = null;
1453                         break;
1454                     }
1455                     Log.w(TAG, "HFP to be Connected in AudioOn state");
1456                     if (okToConnect(device)
1457                             && (mConnectedDevicesList.size() < max_hf_connections)) {
1458                         Log.i(TAG, "Incoming Hf accepted");
1459                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1460                                 BluetoothProfile.STATE_DISCONNECTED);
1461                         synchronized (HeadsetStateMachine.this) {
1462                             if (!mConnectedDevicesList.contains(device)) {
1463                                 mCurrentDevice = device;
1464                                 mConnectedDevicesList.add(device);
1465                                 Log.d(TAG, "device " + device.getAddress()
1466                                                 + " is added in AudioOn state");
1467                             }
1468                         }
1469                         configAudioParameters(device);
1470                     } else {
1471                         // reject the connection and stay in Connected state itself
1472                         Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
1473                                         + " bondState=" + device.getBondState());
1474                         disconnectHfpNative(getByteAddress(device));
1475                     }
1476                     break;
1477                 default:
1478                     Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1479                     break;
1480             }
1481         }
1482 
1483         // in AudioOn state
processAudioEvent(int state, BluetoothDevice device)1484         private void processAudioEvent(int state, BluetoothDevice device) {
1485             if (!mConnectedDevicesList.contains(device)) {
1486                 Log.e(TAG, "Audio changed on disconnected device: " + device);
1487                 return;
1488             }
1489 
1490             switch (state) {
1491                 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1492                     if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1493                         mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1494                         mAudioManager.setBluetoothScoOn(false);
1495                         broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1496                                 BluetoothHeadset.STATE_AUDIO_CONNECTED);
1497                     }
1498                     transitionTo(mConnected);
1499                     break;
1500                 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1501                     // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset?
1502                     // broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING,
1503                     //                    BluetoothHeadset.STATE_AUDIO_CONNECTED);
1504                     break;
1505                 default:
1506                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1507                     break;
1508             }
1509         }
1510 
processSlcConnected()1511         private void processSlcConnected() {
1512             mSlcConnected = true;
1513             if (mPhoneProxy != null) {
1514                 try {
1515                     mPhoneProxy.queryPhoneState();
1516                 } catch (RemoteException e) {
1517                     Log.e(TAG, Log.getStackTraceString(new Throwable()));
1518                 }
1519             } else {
1520                 Log.e(TAG, "Handsfree phone proxy null for query phone state");
1521             }
1522         }
1523 
processIntentScoVolume(Intent intent, BluetoothDevice device)1524         private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
1525             int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
1526             if (mPhoneState.getSpeakerVolume() != volumeValue) {
1527                 mPhoneState.setSpeakerVolume(volumeValue);
1528                 setVolumeNative(
1529                         HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue, getByteAddress(device));
1530             }
1531         }
1532 
processMultiHFConnected(BluetoothDevice device)1533         private void processMultiHFConnected(BluetoothDevice device) {
1534             log("AudioOn state: processMultiHFConnected");
1535             /* Assign the current activedevice again if the disconnected
1536                           device equals to the current active device */
1537             if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1538                 int deviceSize = mConnectedDevicesList.size();
1539                 mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1);
1540             }
1541             if (mAudioState != BluetoothHeadset.STATE_AUDIO_CONNECTED) transitionTo(mConnected);
1542 
1543             log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice);
1544             log("AudioOn state: processMultiHFConnected ,"
1545                     + "fake broadcasting for mCurrentDevice");
1546             broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1547                     BluetoothProfile.STATE_DISCONNECTED);
1548         }
1549     }
1550 
1551     /* Add MultiHFPending state when atleast 1 HS is connected
1552             and disconnect/connect new HS */
1553     private class MultiHFPending extends State {
1554         @Override
enter()1555         public void enter() {
1556             log("Enter MultiHFPending: " + getCurrentMessage().what + ", size: "
1557                     + mConnectedDevicesList.size());
1558         }
1559 
1560         @Override
processMessage(Message message)1561         public boolean processMessage(Message message) {
1562             log("MultiHFPending process message: " + message.what + ", size: "
1563                     + mConnectedDevicesList.size());
1564 
1565             boolean retValue = HANDLED;
1566             switch (message.what) {
1567                 case CONNECT:
1568                     deferMessage(message);
1569                     break;
1570 
1571                 case CONNECT_AUDIO:
1572                     if (mCurrentDevice != null) {
1573                         connectAudioNative(getByteAddress(mCurrentDevice));
1574                     }
1575                     break;
1576                 case CONNECT_TIMEOUT:
1577                     onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
1578                             getByteAddress(mTargetDevice));
1579                     break;
1580 
1581                 case DISCONNECT_AUDIO:
1582                     if (mActiveScoDevice != null) {
1583                         if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1584                             Log.d(TAG, "MultiHFPending, Disconnecting SCO audio for "
1585                                             + mActiveScoDevice);
1586                         } else {
1587                             Log.e(TAG, "disconnectAudioNative failed"
1588                                             + "for device = " + mActiveScoDevice);
1589                         }
1590                     }
1591                     break;
1592                 case DISCONNECT:
1593                     BluetoothDevice device = (BluetoothDevice) message.obj;
1594                     if (mConnectedDevicesList.contains(device) && mTargetDevice != null
1595                             && mTargetDevice.equals(device)) {
1596                         // cancel connection to the mTargetDevice
1597                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1598                                 BluetoothProfile.STATE_CONNECTING);
1599                         synchronized (HeadsetStateMachine.this) {
1600                             mTargetDevice = null;
1601                         }
1602                     } else {
1603                         deferMessage(message);
1604                     }
1605                     break;
1606                 case VOICE_RECOGNITION_START:
1607                     device = (BluetoothDevice) message.obj;
1608                     if (mConnectedDevicesList.contains(device)) {
1609                         processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
1610                     }
1611                     break;
1612                 case VOICE_RECOGNITION_STOP:
1613                     device = (BluetoothDevice) message.obj;
1614                     if (mConnectedDevicesList.contains(device)) {
1615                         processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
1616                     }
1617                     break;
1618                 case INTENT_SCO_VOLUME_CHANGED:
1619                     if (mActiveScoDevice != null) {
1620                         processIntentScoVolume((Intent) message.obj, mActiveScoDevice);
1621                     }
1622                     break;
1623                 case INTENT_BATTERY_CHANGED:
1624                     processIntentBatteryChanged((Intent) message.obj);
1625                     break;
1626                 case CALL_STATE_CHANGED:
1627                     processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
1628                     break;
1629                 case DEVICE_STATE_CHANGED:
1630                     processDeviceStateChanged((HeadsetDeviceState) message.obj);
1631                     break;
1632                 case SEND_CCLC_RESPONSE:
1633                     processSendClccResponse((HeadsetClccResponse) message.obj);
1634                     break;
1635                 case CLCC_RSP_TIMEOUT: {
1636                     device = (BluetoothDevice) message.obj;
1637                     clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
1638                 } break;
1639                 case DIALING_OUT_TIMEOUT:
1640                     if (mDialingOut) {
1641                         device = (BluetoothDevice) message.obj;
1642                         mDialingOut = false;
1643                         atResponseCodeNative(
1644                                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
1645                     }
1646                     break;
1647                 case VIRTUAL_CALL_START:
1648                     device = (BluetoothDevice) message.obj;
1649                     if (mConnectedDevicesList.contains(device)) {
1650                         initiateScoUsingVirtualVoiceCall();
1651                     }
1652                     break;
1653                 case VIRTUAL_CALL_STOP:
1654                     device = (BluetoothDevice) message.obj;
1655                     if (mConnectedDevicesList.contains(device)) {
1656                         terminateScoUsingVirtualVoiceCall();
1657                     }
1658                     break;
1659                 case START_VR_TIMEOUT:
1660                     if (mWaitingForVoiceRecognition) {
1661                         device = (BluetoothDevice) message.obj;
1662                         mWaitingForVoiceRecognition = false;
1663                         Log.e(TAG, "Timeout waiting for voice"
1664                                         + "recognition to start");
1665                         atResponseCodeNative(
1666                                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
1667                     }
1668                     break;
1669                 case STACK_EVENT:
1670                     StackEvent event = (StackEvent) message.obj;
1671                     if (DBG) {
1672                         log("event type: " + event.type);
1673                     }
1674                     switch (event.type) {
1675                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1676                             BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
1677                             if (device1 != null && device1.equals(event.device)) {
1678                                 Log.d(TAG, "remove connect timeout for device = " + device1);
1679                                 removeMessages(CONNECT_TIMEOUT);
1680                             }
1681                             processConnectionEvent(event.valueInt, event.device);
1682                             break;
1683                         case EVENT_TYPE_AUDIO_STATE_CHANGED:
1684                             processAudioEvent(event.valueInt, event.device);
1685                             break;
1686                         case EVENT_TYPE_VR_STATE_CHANGED:
1687                             processVrEvent(event.valueInt, event.device);
1688                             break;
1689                         case EVENT_TYPE_ANSWER_CALL:
1690                             // TODO(BT) could answer call happen on Connected state?
1691                             processAnswerCall(event.device);
1692                             break;
1693                         case EVENT_TYPE_HANGUP_CALL:
1694                             // TODO(BT) could hangup call happen on Connected state?
1695                             processHangupCall(event.device);
1696                             break;
1697                         case EVENT_TYPE_VOLUME_CHANGED:
1698                             processVolumeEvent(event.valueInt, event.valueInt2, event.device);
1699                             break;
1700                         case EVENT_TYPE_DIAL_CALL:
1701                             processDialCall(event.valueString, event.device);
1702                             break;
1703                         case EVENT_TYPE_SEND_DTMF:
1704                             processSendDtmf(event.valueInt, event.device);
1705                             break;
1706                         case EVENT_TYPE_NOICE_REDUCTION:
1707                             processNoiceReductionEvent(event.valueInt, event.device);
1708                             break;
1709                         case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1710                             processSubscriberNumberRequest(event.device);
1711                             break;
1712                         case EVENT_TYPE_AT_CIND:
1713                             processAtCind(event.device);
1714                             break;
1715                         case EVENT_TYPE_AT_CHLD:
1716                             processAtChld(event.valueInt, event.device);
1717                             break;
1718                         case EVENT_TYPE_AT_COPS:
1719                             processAtCops(event.device);
1720                             break;
1721                         case EVENT_TYPE_AT_CLCC:
1722                             processAtClcc(event.device);
1723                             break;
1724                         case EVENT_TYPE_UNKNOWN_AT:
1725                             processUnknownAt(event.valueString, event.device);
1726                             break;
1727                         case EVENT_TYPE_KEY_PRESSED:
1728                             processKeyPressed(event.device);
1729                             break;
1730                         case EVENT_TYPE_BIND:
1731                             processAtBind(event.valueString, event.device);
1732                             break;
1733                         case EVENT_TYPE_BIEV:
1734                             processAtBiev(event.valueInt, event.valueInt2, event.device);
1735                             break;
1736                         default:
1737                             Log.e(TAG, "Unexpected event: " + event.type);
1738                             break;
1739                     }
1740                     break;
1741                 default:
1742                     return NOT_HANDLED;
1743             }
1744             return retValue;
1745         }
1746 
1747         // in MultiHFPending state
processConnectionEvent(int state, BluetoothDevice device)1748         private void processConnectionEvent(int state, BluetoothDevice device) {
1749             Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
1750             switch (state) {
1751                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1752                     if (mConnectedDevicesList.contains(device)) {
1753                         if (mMultiDisconnectDevice != null
1754                                 && mMultiDisconnectDevice.equals(device)) {
1755                             mMultiDisconnectDevice = null;
1756 
1757                             synchronized (HeadsetStateMachine.this) {
1758                                 mConnectedDevicesList.remove(device);
1759                                 mHeadsetAudioParam.remove(device);
1760                                 mHeadsetBrsf.remove(device);
1761                                 Log.d(TAG, "device " + device.getAddress()
1762                                                 + " is removed in MultiHFPending state");
1763                                 broadcastConnectionState(device,
1764                                         BluetoothProfile.STATE_DISCONNECTED,
1765                                         BluetoothProfile.STATE_DISCONNECTING);
1766                             }
1767 
1768                             if (mTargetDevice != null) {
1769                                 if (!connectHfpNative(getByteAddress(mTargetDevice))) {
1770                                     broadcastConnectionState(mTargetDevice,
1771                                             BluetoothProfile.STATE_DISCONNECTED,
1772                                             BluetoothProfile.STATE_CONNECTING);
1773                                     synchronized (HeadsetStateMachine.this) {
1774                                         mTargetDevice = null;
1775                                         if (mConnectedDevicesList.size() == 0) {
1776                                             // Should be not in this state since it has at least
1777                                             // one HF connected in MultiHFPending state
1778                                             Log.d(TAG,
1779                                                     "Should be not in this state, error handling");
1780                                             transitionTo(mDisconnected);
1781                                         } else {
1782                                             processMultiHFConnected(device);
1783                                         }
1784                                     }
1785                                 }
1786                             } else {
1787                                 synchronized (HeadsetStateMachine.this) {
1788                                     mIncomingDevice = null;
1789                                     if (mConnectedDevicesList.size() == 0) {
1790                                         transitionTo(mDisconnected);
1791                                     } else {
1792                                         processMultiHFConnected(device);
1793                                     }
1794                                 }
1795                             }
1796                         } else {
1797                             /* Another HF disconnected when one HF is connecting */
1798                             synchronized (HeadsetStateMachine.this) {
1799                                 mConnectedDevicesList.remove(device);
1800                                 mHeadsetAudioParam.remove(device);
1801                                 mHeadsetBrsf.remove(device);
1802                                 Log.d(TAG, "device " + device.getAddress()
1803                                                 + " is removed in MultiHFPending state");
1804                             }
1805                             broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1806                                     BluetoothProfile.STATE_CONNECTED);
1807                         }
1808                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1809                         broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
1810                                 BluetoothProfile.STATE_CONNECTING);
1811                         synchronized (HeadsetStateMachine.this) {
1812                             mTargetDevice = null;
1813                             if (mConnectedDevicesList.size() == 0) {
1814                                 transitionTo(mDisconnected);
1815                             } else {
1816                                 if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1817                                     transitionTo(mAudioOn);
1818                                 else
1819                                     transitionTo(mConnected);
1820                             }
1821                         }
1822                     } else {
1823                         Log.e(TAG, "Unknown device Disconnected: " + device);
1824                     }
1825                     break;
1826                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1827                     /* Outgoing disconnection for device failed */
1828                     if (mConnectedDevicesList.contains(device)) {
1829                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1830                                 BluetoothProfile.STATE_DISCONNECTING);
1831                         if (mTargetDevice != null) {
1832                             broadcastConnectionState(mTargetDevice,
1833                                     BluetoothProfile.STATE_DISCONNECTED,
1834                                     BluetoothProfile.STATE_CONNECTING);
1835                         }
1836                         synchronized (HeadsetStateMachine.this) {
1837                             mTargetDevice = null;
1838                             if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1839                                 transitionTo(mAudioOn);
1840                             else
1841                                 transitionTo(mConnected);
1842                         }
1843                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1844                         synchronized (HeadsetStateMachine.this) {
1845                             mCurrentDevice = device;
1846                             mConnectedDevicesList.add(device);
1847                             Log.d(TAG, "device " + device.getAddress()
1848                                             + " is added in MultiHFPending state");
1849                             mTargetDevice = null;
1850                             if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1851                                 transitionTo(mAudioOn);
1852                             else
1853                                 transitionTo(mConnected);
1854                         }
1855 
1856                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1857                                 BluetoothProfile.STATE_CONNECTING);
1858                         configAudioParameters(device);
1859                     } else {
1860                         Log.w(TAG, "Some other incoming HF connected"
1861                                         + "in Multi Pending state");
1862                         if (okToConnect(device)
1863                                 && (mConnectedDevicesList.size() < max_hf_connections)) {
1864                             Log.i(TAG, "Incoming Hf accepted");
1865                             broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1866                                     BluetoothProfile.STATE_DISCONNECTED);
1867                             synchronized (HeadsetStateMachine.this) {
1868                                 if (!mConnectedDevicesList.contains(device)) {
1869                                     mCurrentDevice = device;
1870                                     mConnectedDevicesList.add(device);
1871                                     Log.d(TAG, "device " + device.getAddress()
1872                                                     + " is added in MultiHFPending state");
1873                                 }
1874                             }
1875                             configAudioParameters(device);
1876                         } else {
1877                             // reject the connection and stay in Pending state itself
1878                             Log.i(TAG, "Incoming Hf rejected. priority="
1879                                             + mService.getPriority(device) + " bondState="
1880                                             + device.getBondState());
1881                             disconnectHfpNative(getByteAddress(device));
1882                         }
1883                     }
1884                     break;
1885                 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1886                     processSlcConnected();
1887                     break;
1888                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
1889                     if (mConnectedDevicesList.contains(device)) {
1890                         Log.e(TAG, "current device tries to connect back");
1891                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1892                         if (DBG) {
1893                             log("Stack and target device are connecting");
1894                         }
1895                     } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
1896                         Log.e(TAG, "Another connecting event on"
1897                                         + "the incoming device");
1898                     }
1899                     break;
1900                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
1901                     if (mConnectedDevicesList.contains(device)) {
1902                         if (DBG) {
1903                             log("stack is disconnecting mCurrentDevice");
1904                         }
1905                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1906                         Log.e(TAG, "TargetDevice is getting disconnected");
1907                     } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
1908                         Log.e(TAG, "IncomingDevice is getting disconnected");
1909                     } else {
1910                         Log.e(TAG, "Disconnecting unknow device: " + device);
1911                     }
1912                     break;
1913                 default:
1914                     Log.e(TAG, "Incorrect state: " + state);
1915                     break;
1916             }
1917         }
1918 
processAudioEvent(int state, BluetoothDevice device)1919         private void processAudioEvent(int state, BluetoothDevice device) {
1920             if (!mConnectedDevicesList.contains(device)) {
1921                 Log.e(TAG, "Audio changed on disconnected device: " + device);
1922                 return;
1923             }
1924 
1925             switch (state) {
1926                 case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1927                     if (!isScoAcceptable()) {
1928                         Log.e(TAG, "Audio Connected without any listener");
1929                         disconnectAudioNative(getByteAddress(device));
1930                         break;
1931                     }
1932                     mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
1933                     setAudioParameters(device); /* Set proper Audio Parameters. */
1934                     mAudioManager.setBluetoothScoOn(true);
1935                     mActiveScoDevice = device;
1936                     broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1937                             BluetoothHeadset.STATE_AUDIO_CONNECTING);
1938                     /* The state should be still in MultiHFPending state when
1939                        audio connected since other device is still connecting/
1940                        disconnecting */
1941                     break;
1942                 case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1943                     mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
1944                     broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
1945                             BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1946                     break;
1947                 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1948                     if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1949                         mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1950                         mAudioManager.setBluetoothScoOn(false);
1951                         broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1952                                 BluetoothHeadset.STATE_AUDIO_CONNECTED);
1953                     }
1954                     /* The state should be still in MultiHFPending state when audio
1955                        disconnected since other device is still connecting/
1956                        disconnecting */
1957                     break;
1958 
1959                 default:
1960                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1961                     break;
1962             }
1963         }
1964 
processSlcConnected()1965         private void processSlcConnected() {
1966             mSlcConnected = true;
1967             if (mPhoneProxy != null) {
1968                 try {
1969                     mPhoneProxy.queryPhoneState();
1970                 } catch (RemoteException e) {
1971                     Log.e(TAG, Log.getStackTraceString(new Throwable()));
1972                 }
1973             } else {
1974                 Log.e(TAG, "Handsfree phone proxy null for query phone state");
1975             }
1976         }
1977 
processMultiHFConnected(BluetoothDevice device)1978         private void processMultiHFConnected(BluetoothDevice device) {
1979             log("MultiHFPending state: processMultiHFConnected");
1980             if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1981                 log("mActiveScoDevice is disconnected, setting it to null");
1982                 mActiveScoDevice = null;
1983             }
1984             /* Assign the current activedevice again if the disconnected
1985                device equals to the current active device */
1986             if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1987                 int deviceSize = mConnectedDevicesList.size();
1988                 mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1);
1989             }
1990             // The disconnected device is not current active device
1991             if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1992                 transitionTo(mAudioOn);
1993             else
1994                 transitionTo(mConnected);
1995             log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice);
1996             log("MultiHFPending state: processMultiHFConnected ,"
1997                     + "fake broadcasting for mCurrentDevice");
1998             broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1999                     BluetoothProfile.STATE_DISCONNECTED);
2000         }
2001 
processIntentScoVolume(Intent intent, BluetoothDevice device)2002         private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
2003             int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
2004             if (mPhoneState.getSpeakerVolume() != volumeValue) {
2005                 mPhoneState.setSpeakerVolume(volumeValue);
2006                 setVolumeNative(
2007                         HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue, getByteAddress(device));
2008             }
2009         }
2010     }
2011 
2012     private ServiceConnection mConnection = new ServiceConnection() {
2013         public void onServiceConnected(ComponentName className, IBinder service) {
2014             if (DBG) Log.d(TAG, "Proxy object connected");
2015             mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service);
2016         }
2017 
2018         public void onServiceDisconnected(ComponentName className) {
2019             if (DBG) Log.d(TAG, "Proxy object disconnected");
2020             mPhoneProxy = null;
2021         }
2022     };
2023 
2024     // HFP Connection state of the device could be changed by the state machine
2025     // in separate thread while this method is executing.
getConnectionState(BluetoothDevice device)2026     int getConnectionState(BluetoothDevice device) {
2027         if (getCurrentState() == mDisconnected) {
2028             if (DBG) Log.d(TAG, "currentState is Disconnected");
2029             return BluetoothProfile.STATE_DISCONNECTED;
2030         }
2031 
2032         synchronized (this) {
2033             IState currentState = getCurrentState();
2034             if (DBG) Log.d(TAG, "currentState = " + currentState);
2035             if (currentState == mPending) {
2036                 if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
2037                     return BluetoothProfile.STATE_CONNECTING;
2038                 }
2039                 if (mConnectedDevicesList.contains(device)) {
2040                     return BluetoothProfile.STATE_DISCONNECTING;
2041                 }
2042                 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
2043                     return BluetoothProfile.STATE_CONNECTING; // incoming connection
2044                 }
2045                 return BluetoothProfile.STATE_DISCONNECTED;
2046             }
2047 
2048             if (currentState == mMultiHFPending) {
2049                 if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
2050                     return BluetoothProfile.STATE_CONNECTING;
2051                 }
2052                 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
2053                     return BluetoothProfile.STATE_CONNECTING; // incoming connection
2054                 }
2055                 if (mConnectedDevicesList.contains(device)) {
2056                     if ((mMultiDisconnectDevice != null)
2057                             && (!mMultiDisconnectDevice.equals(device))) {
2058                         // The device is still connected
2059                         return BluetoothProfile.STATE_CONNECTED;
2060                     }
2061                     return BluetoothProfile.STATE_DISCONNECTING;
2062                 }
2063                 return BluetoothProfile.STATE_DISCONNECTED;
2064             }
2065 
2066             if (currentState == mConnected || currentState == mAudioOn) {
2067                 if (mConnectedDevicesList.contains(device)) {
2068                     return BluetoothProfile.STATE_CONNECTED;
2069                 }
2070                 return BluetoothProfile.STATE_DISCONNECTED;
2071             } else {
2072                 Log.e(TAG, "Bad currentState: " + currentState);
2073                 return BluetoothProfile.STATE_DISCONNECTED;
2074             }
2075         }
2076     }
2077 
getConnectedDevices()2078     List<BluetoothDevice> getConnectedDevices() {
2079         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
2080         synchronized (this) {
2081             for (int i = 0; i < mConnectedDevicesList.size(); i++)
2082                 devices.add(mConnectedDevicesList.get(i));
2083         }
2084 
2085         return devices;
2086     }
2087 
isAudioOn()2088     boolean isAudioOn() {
2089         return (getCurrentState() == mAudioOn);
2090     }
2091 
isAudioConnected(BluetoothDevice device)2092     boolean isAudioConnected(BluetoothDevice device) {
2093         synchronized (this) {
2094             /*  Additional check for audio state included for the case when PhoneApp queries
2095             Bluetooth Audio state, before we receive the close event from the stack for the
2096             sco disconnect issued in AudioOn state. This was causing a mismatch in the
2097             Incall screen UI. */
2098 
2099             if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)
2100                     && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
2101                 return true;
2102             }
2103         }
2104         return false;
2105     }
2106 
isSlcConnected()2107     boolean isSlcConnected() {
2108         return mSlcConnected;
2109     }
2110 
setAudioRouteAllowed(boolean allowed)2111     public void setAudioRouteAllowed(boolean allowed) {
2112         mAudioRouteAllowed = allowed;
2113     }
2114 
getAudioRouteAllowed()2115     public boolean getAudioRouteAllowed() {
2116         return mAudioRouteAllowed;
2117     }
2118 
getAudioState(BluetoothDevice device)2119     int getAudioState(BluetoothDevice device) {
2120         synchronized (this) {
2121             if (mConnectedDevicesList.size() == 0) {
2122                 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
2123             }
2124         }
2125         return mAudioState;
2126     }
2127 
processVrEvent(int state, BluetoothDevice device)2128     private void processVrEvent(int state, BluetoothDevice device) {
2129         if (device == null) {
2130             Log.w(TAG, "processVrEvent device is null");
2131             return;
2132         }
2133         Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: "
2134                         + mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: "
2135                         + mWaitingForVoiceRecognition + " isInCall: " + isInCall());
2136         if (state == HeadsetHalConstants.VR_STATE_STARTED) {
2137             if (!isVirtualCallInProgress() && !isInCall()) {
2138                 IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
2139                         ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
2140                 if (dic != null) {
2141                     try {
2142                         dic.exitIdle("voice-command");
2143                     } catch (RemoteException e) {
2144                     }
2145                 }
2146                 try {
2147                     mService.startActivity(sVoiceCommandIntent);
2148                 } catch (ActivityNotFoundException e) {
2149                     atResponseCodeNative(
2150                             HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2151                     return;
2152                 }
2153                 expectVoiceRecognition(device);
2154             } else {
2155                 // send error response if call is ongoing
2156                 atResponseCodeNative(
2157                         HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2158                 return;
2159             }
2160         } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
2161             if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) {
2162                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2163                 mVoiceRecognitionStarted = false;
2164                 mWaitingForVoiceRecognition = false;
2165                 if (!isInCall() && (mActiveScoDevice != null)) {
2166                     disconnectAudioNative(getByteAddress(mActiveScoDevice));
2167                     mAudioManager.setParameters("A2dpSuspended=false");
2168                 }
2169             } else {
2170                 atResponseCodeNative(
2171                         HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2172             }
2173         } else {
2174             Log.e(TAG, "Bad Voice Recognition state: " + state);
2175         }
2176     }
2177 
processLocalVrEvent(int state)2178     private void processLocalVrEvent(int state) {
2179         BluetoothDevice device = null;
2180         if (state == HeadsetHalConstants.VR_STATE_STARTED) {
2181             boolean needAudio = true;
2182             if (mVoiceRecognitionStarted || isInCall()) {
2183                 Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall()
2184                                 + " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
2185                 return;
2186             }
2187             mVoiceRecognitionStarted = true;
2188 
2189             if (mWaitingForVoiceRecognition) {
2190                 device = getDeviceForMessage(START_VR_TIMEOUT);
2191                 if (device == null) return;
2192 
2193                 Log.d(TAG, "Voice recognition started successfully");
2194                 mWaitingForVoiceRecognition = false;
2195                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2196                 removeMessages(START_VR_TIMEOUT);
2197             } else {
2198                 Log.d(TAG, "Voice recognition started locally");
2199                 needAudio = startVoiceRecognitionNative(getByteAddress(mCurrentDevice));
2200                 if (mCurrentDevice != null) device = mCurrentDevice;
2201             }
2202 
2203             if (needAudio && !isAudioOn()) {
2204                 Log.d(TAG, "Initiating audio connection for Voice Recognition");
2205                 // At this stage, we need to be sure that AVDTP is not streaming. This is needed
2206                 // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in
2207                 // streaming state while a SCO connection is established.
2208                 // This is needed for VoiceDial scenario alone and not for
2209                 // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE
2210                 // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed.
2211                 // Whereas for VoiceDial we want to activate the SCO connection but we are still
2212                 // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream
2213                 mAudioManager.setParameters("A2dpSuspended=true");
2214                 if (device != null) {
2215                     connectAudioNative(getByteAddress(device));
2216                 } else {
2217                     Log.e(TAG, "device not found for VR");
2218                 }
2219             }
2220 
2221             if (mStartVoiceRecognitionWakeLock.isHeld()) {
2222                 mStartVoiceRecognitionWakeLock.release();
2223             }
2224         } else {
2225             Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: "
2226                             + mVoiceRecognitionStarted + " mWaitingForVoiceRecognition: "
2227                             + mWaitingForVoiceRecognition);
2228             if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) {
2229                 mVoiceRecognitionStarted = false;
2230                 mWaitingForVoiceRecognition = false;
2231 
2232                 if (stopVoiceRecognitionNative(getByteAddress(mCurrentDevice)) && !isInCall()
2233                         && mActiveScoDevice != null) {
2234                     disconnectAudioNative(getByteAddress(mActiveScoDevice));
2235                     mAudioManager.setParameters("A2dpSuspended=false");
2236                 }
2237             }
2238         }
2239     }
2240 
expectVoiceRecognition(BluetoothDevice device)2241     private synchronized void expectVoiceRecognition(BluetoothDevice device) {
2242         mWaitingForVoiceRecognition = true;
2243         Message m = obtainMessage(START_VR_TIMEOUT);
2244         m.obj = getMatchingDevice(device);
2245         sendMessageDelayed(m, START_VR_TIMEOUT_VALUE);
2246 
2247         if (!mStartVoiceRecognitionWakeLock.isHeld()) {
2248             mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE);
2249         }
2250     }
2251 
getDevicesMatchingConnectionStates(int[] states)2252     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
2253         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
2254         Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
2255         int connectionState;
2256         synchronized (this) {
2257             for (BluetoothDevice device : bondedDevices) {
2258                 ParcelUuid[] featureUuids = device.getUuids();
2259                 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
2260                     continue;
2261                 }
2262                 connectionState = getConnectionState(device);
2263                 for (int i = 0; i < states.length; i++) {
2264                     if (connectionState == states[i]) {
2265                         deviceList.add(device);
2266                     }
2267                 }
2268             }
2269         }
2270         return deviceList;
2271     }
2272 
getDeviceForMessage(int what)2273     private BluetoothDevice getDeviceForMessage(int what) {
2274         if (what == CONNECT_TIMEOUT) {
2275             log("getDeviceForMessage: returning mTargetDevice for what=" + what);
2276             return mTargetDevice;
2277         }
2278         if (mConnectedDevicesList.size() == 0) {
2279             log("getDeviceForMessage: No connected device. what=" + what);
2280             return null;
2281         }
2282         for (BluetoothDevice device : mConnectedDevicesList) {
2283             if (getHandler().hasMessages(what, device)) {
2284                 log("getDeviceForMessage: returning " + device);
2285                 return device;
2286             }
2287         }
2288         log("getDeviceForMessage: No matching device for " + what + ". Returning null");
2289         return null;
2290     }
2291 
getMatchingDevice(BluetoothDevice device)2292     private BluetoothDevice getMatchingDevice(BluetoothDevice device) {
2293         for (BluetoothDevice matchingDevice : mConnectedDevicesList) {
2294             if (matchingDevice.equals(device)) {
2295                 return matchingDevice;
2296             }
2297         }
2298         return null;
2299     }
2300 
2301     // This method does not check for error conditon (newState == prevState)
broadcastConnectionState(BluetoothDevice device, int newState, int prevState)2302     private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
2303         log("Connection state " + device + ": " + prevState + "->" + newState);
2304         if (prevState == BluetoothProfile.STATE_CONNECTED) {
2305             // Headset is disconnecting, stop Virtual call if active.
2306             terminateScoUsingVirtualVoiceCall();
2307         }
2308 
2309         Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
2310         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2311         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2312         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2313         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
2314         mService.sendBroadcastAsUser(intent, UserHandle.ALL,
2315                 HeadsetService.BLUETOOTH_PERM);
2316     }
2317 
broadcastAudioState(BluetoothDevice device, int newState, int prevState)2318     private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
2319         if (prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
2320             // When SCO gets disconnected during call transfer, Virtual call
2321             // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall.
2322             terminateScoUsingVirtualVoiceCall();
2323         }
2324         Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
2325         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2326         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2327         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2328         mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
2329         log("Audio state " + device + ": " + prevState + "->" + newState);
2330     }
2331 
2332     /*
2333      * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
2334      */
broadcastVendorSpecificEventIntent(String command, int companyId, int commandType, Object[] arguments, BluetoothDevice device)2335     private void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType,
2336             Object[] arguments, BluetoothDevice device) {
2337         log("broadcastVendorSpecificEventIntent(" + command + ")");
2338         Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
2339         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
2340         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType);
2341         // assert: all elements of args are Serializable
2342         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
2343         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2344 
2345         intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
2346                 + Integer.toString(companyId));
2347 
2348         mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
2349     }
2350 
configAudioParameters(BluetoothDevice device)2351     private void configAudioParameters(BluetoothDevice device) {
2352         // Reset NREC on connect event. Headset will override later
2353         HashMap<String, Integer> AudioParamConfig = new HashMap<String, Integer>();
2354         AudioParamConfig.put("NREC", 1);
2355         mHeadsetAudioParam.put(device, AudioParamConfig);
2356         mAudioManager.setParameters(
2357                 HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" + HEADSET_NREC + "=on");
2358         Log.d(TAG, "configAudioParameters for device:" + device + " are: nrec = "
2359                         + AudioParamConfig.get("NREC"));
2360     }
2361 
setAudioParameters(BluetoothDevice device)2362     private void setAudioParameters(BluetoothDevice device) {
2363         // 1. update nrec value
2364         // 2. update headset name
2365         int mNrec = 0;
2366         HashMap<String, Integer> AudioParam = mHeadsetAudioParam.get(device);
2367         if (AudioParam != null && !AudioParam.isEmpty()) {
2368             mNrec = AudioParam.get("NREC");
2369         } else {
2370             Log.e(TAG, "setAudioParameters: AudioParam not found");
2371         }
2372 
2373         if (mNrec == 1) {
2374             Log.d(TAG, "Set NREC: 1 for device:" + device);
2375             mAudioManager.setParameters(HEADSET_NREC + "=on");
2376         } else {
2377             Log.d(TAG, "Set NREC: 0 for device:" + device);
2378             mAudioManager.setParameters(HEADSET_NREC + "=off");
2379         }
2380         mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device));
2381     }
2382 
parseUnknownAt(String atString)2383     private String parseUnknownAt(String atString) {
2384         StringBuilder atCommand = new StringBuilder(atString.length());
2385         String result = null;
2386 
2387         for (int i = 0; i < atString.length(); i++) {
2388             char c = atString.charAt(i);
2389             if (c == '"') {
2390                 int j = atString.indexOf('"', i + 1); // search for closing "
2391                 if (j == -1) { // unmatched ", insert one.
2392                     atCommand.append(atString.substring(i, atString.length()));
2393                     atCommand.append('"');
2394                     break;
2395                 }
2396                 atCommand.append(atString.substring(i, j + 1));
2397                 i = j;
2398             } else if (c != ' ') {
2399                 atCommand.append(Character.toUpperCase(c));
2400             }
2401         }
2402         result = atCommand.toString();
2403         return result;
2404     }
2405 
getAtCommandType(String atCommand)2406     private int getAtCommandType(String atCommand) {
2407         int commandType = mPhonebook.TYPE_UNKNOWN;
2408         String atString = null;
2409         atCommand = atCommand.trim();
2410         if (atCommand.length() > 5) {
2411             atString = atCommand.substring(5);
2412             if (atString.startsWith("?")) // Read
2413                 commandType = mPhonebook.TYPE_READ;
2414             else if (atString.startsWith("=?")) // Test
2415                 commandType = mPhonebook.TYPE_TEST;
2416             else if (atString.startsWith("=")) // Set
2417                 commandType = mPhonebook.TYPE_SET;
2418             else
2419                 commandType = mPhonebook.TYPE_UNKNOWN;
2420         }
2421         return commandType;
2422     }
2423 
2424     /* Method to check if Virtual Call in Progress */
isVirtualCallInProgress()2425     private boolean isVirtualCallInProgress() {
2426         return mVirtualCallStarted;
2427     }
2428 
setVirtualCallInProgress(boolean state)2429     void setVirtualCallInProgress(boolean state) {
2430         mVirtualCallStarted = state;
2431     }
2432 
2433     /* NOTE: Currently the VirtualCall API does not support handling of
2434     call transfers. If it is initiated from the handsfree device,
2435     HeadsetStateMachine will end the virtual call by calling
2436     terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */
initiateScoUsingVirtualVoiceCall()2437     synchronized boolean initiateScoUsingVirtualVoiceCall() {
2438         if (DBG) log("initiateScoUsingVirtualVoiceCall: Received");
2439         // 1. Check if the SCO state is idle
2440         if (isInCall() || mVoiceRecognitionStarted) {
2441             Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress.");
2442             return false;
2443         }
2444 
2445         // 2. Send virtual phone state changed to initialize SCO
2446         processCallState(
2447                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true);
2448         processCallState(
2449                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true);
2450         processCallState(
2451                 new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
2452         setVirtualCallInProgress(true);
2453         // Done
2454         if (DBG) log("initiateScoUsingVirtualVoiceCall: Done");
2455         return true;
2456     }
2457 
terminateScoUsingVirtualVoiceCall()2458     synchronized boolean terminateScoUsingVirtualVoiceCall() {
2459         if (DBG) log("terminateScoUsingVirtualVoiceCall: Received");
2460 
2461         if (!isVirtualCallInProgress()) {
2462             Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"
2463                             + "No present call to terminate");
2464             return false;
2465         }
2466 
2467         // 2. Send virtual phone state changed to close SCO
2468         processCallState(
2469                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
2470         setVirtualCallInProgress(false);
2471         // Done
2472         if (DBG) log("terminateScoUsingVirtualVoiceCall: Done");
2473         return true;
2474     }
2475 
processAnswerCall(BluetoothDevice device)2476     private void processAnswerCall(BluetoothDevice device) {
2477         if (device == null) {
2478             Log.w(TAG, "processAnswerCall device is null");
2479             return;
2480         }
2481 
2482         if (mPhoneProxy != null) {
2483             try {
2484                 mPhoneProxy.answerCall();
2485             } catch (RemoteException e) {
2486                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
2487             }
2488         } else {
2489             Log.e(TAG, "Handsfree phone proxy null for answering call");
2490         }
2491     }
2492 
processHangupCall(BluetoothDevice device)2493     private void processHangupCall(BluetoothDevice device) {
2494         if (device == null) {
2495             Log.w(TAG, "processHangupCall device is null");
2496             return;
2497         }
2498         // Close the virtual call if active. Virtual call should be
2499         // terminated for CHUP callback event
2500         if (isVirtualCallInProgress()) {
2501             terminateScoUsingVirtualVoiceCall();
2502         } else {
2503             if (mPhoneProxy != null) {
2504                 try {
2505                     mPhoneProxy.hangupCall();
2506                 } catch (RemoteException e) {
2507                     Log.e(TAG, Log.getStackTraceString(new Throwable()));
2508                 }
2509             } else {
2510                 Log.e(TAG, "Handsfree phone proxy null for hanging up call");
2511             }
2512         }
2513     }
2514 
processDialCall(String number, BluetoothDevice device)2515     private void processDialCall(String number, BluetoothDevice device) {
2516         if (device == null) {
2517             Log.w(TAG, "processDialCall device is null");
2518             return;
2519         }
2520 
2521         String dialNumber;
2522         if (mDialingOut) {
2523             if (DBG) log("processDialCall, already dialling");
2524             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2525             return;
2526         }
2527         if ((number == null) || (number.length() == 0)) {
2528             dialNumber = mPhonebook.getLastDialledNumber();
2529             if (dialNumber == null) {
2530                 if (DBG) log("processDialCall, last dial number null");
2531                 atResponseCodeNative(
2532                         HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2533                 return;
2534             }
2535         } else if (number.charAt(0) == '>') {
2536             // Yuck - memory dialling requested.
2537             // Just dial last number for now
2538             if (number.startsWith(">9999")) { // for PTS test
2539                 atResponseCodeNative(
2540                         HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2541                 return;
2542             }
2543             if (DBG) log("processDialCall, memory dial do last dial for now");
2544             dialNumber = mPhonebook.getLastDialledNumber();
2545             if (dialNumber == null) {
2546                 if (DBG) log("processDialCall, last dial number null");
2547                 atResponseCodeNative(
2548                         HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2549                 return;
2550             }
2551         } else {
2552             // Remove trailing ';'
2553             if (number.charAt(number.length() - 1) == ';') {
2554                 number = number.substring(0, number.length() - 1);
2555             }
2556 
2557             dialNumber = PhoneNumberUtils.convertPreDial(number);
2558         }
2559         // Check for virtual call to terminate before sending Call Intent
2560         terminateScoUsingVirtualVoiceCall();
2561 
2562         Intent intent = new Intent(
2563                 Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null));
2564         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2565         mService.startActivity(intent);
2566         // TODO(BT) continue send OK reults code after call starts
2567         //          hold wait lock, start a timer, set wait call flag
2568         //          Get call started indication from bluetooth phone
2569         mDialingOut = true;
2570         Message m = obtainMessage(DIALING_OUT_TIMEOUT);
2571         m.obj = getMatchingDevice(device);
2572         sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE);
2573     }
2574 
processVolumeEvent(int volumeType, int volume, BluetoothDevice device)2575     private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) {
2576         if (device != null && !device.equals(mActiveScoDevice) && mPhoneState.isInCall()) {
2577             Log.w(TAG, "ignore processVolumeEvent");
2578             return;
2579         }
2580 
2581         if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
2582             mPhoneState.setSpeakerVolume(volume);
2583             int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
2584             mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
2585         } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
2586             mPhoneState.setMicVolume(volume);
2587         } else {
2588             Log.e(TAG, "Bad voluem type: " + volumeType);
2589         }
2590     }
2591 
processSendDtmf(int dtmf, BluetoothDevice device)2592     private void processSendDtmf(int dtmf, BluetoothDevice device) {
2593         if (device == null) {
2594             Log.w(TAG, "processSendDtmf device is null");
2595             return;
2596         }
2597 
2598         if (mPhoneProxy != null) {
2599             try {
2600                 mPhoneProxy.sendDtmf(dtmf);
2601             } catch (RemoteException e) {
2602                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
2603             }
2604         } else {
2605             Log.e(TAG, "Handsfree phone proxy null for sending DTMF");
2606         }
2607     }
2608 
processCallState(HeadsetCallState callState)2609     private void processCallState(HeadsetCallState callState) {
2610         processCallState(callState, false);
2611     }
2612 
processCallState(HeadsetCallState callState, boolean isVirtualCall)2613     private void processCallState(HeadsetCallState callState, boolean isVirtualCall) {
2614         mPhoneState.setNumActiveCall(callState.mNumActive);
2615         mPhoneState.setNumHeldCall(callState.mNumHeld);
2616         mPhoneState.setCallState(callState.mCallState);
2617         if (mDialingOut) {
2618             if (callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) {
2619                 BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT);
2620                 if (device == null) {
2621                     return;
2622                 }
2623                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2624                 removeMessages(DIALING_OUT_TIMEOUT);
2625             } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE
2626                     || callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) {
2627                 mDialingOut = false;
2628             }
2629         }
2630 
2631         /* Set ActiveScoDevice to null when call ends */
2632         if ((mActiveScoDevice != null) && !isInCall()
2633                 && callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE)
2634             mActiveScoDevice = null;
2635 
2636         log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld
2637                 + " mCallState: " + callState.mCallState);
2638         log("mNumber: " + callState.mNumber + " mType: " + callState.mType);
2639 
2640         if (isVirtualCall) {
2641             // virtual call state update
2642             if (getCurrentState() != mDisconnected) {
2643                 phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
2644                         callState.mCallState, callState.mNumber, callState.mType);
2645             }
2646         } else {
2647             // circuit-switch voice call update
2648             // stop virtual voice call if there is a CSV call ongoing
2649             if (callState.mNumActive > 0 || callState.mNumHeld > 0
2650                     || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) {
2651                 terminateScoUsingVirtualVoiceCall();
2652             }
2653 
2654             // Specific handling for case of starting MO/MT call while VOIP
2655             // ongoing, terminateScoUsingVirtualVoiceCall() resets callState
2656             // INCOMING/DIALING to IDLE. Some HS send AT+CIND? to read call
2657             // and get wrong value of callsetup. This case is hit only
2658             // SCO for VOIP call is not terminated via SDK API call.
2659             if (mPhoneState.getCallState() != callState.mCallState) {
2660                 mPhoneState.setCallState(callState.mCallState);
2661             }
2662 
2663             // at this step: if there is virtual call ongoing, it means there is no CSV call
2664             // let virtual call continue and skip phone state update
2665             if (!isVirtualCallInProgress()) {
2666                 if (getCurrentState() != mDisconnected) {
2667                     phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
2668                             callState.mCallState, callState.mNumber, callState.mType);
2669                 }
2670             }
2671         }
2672     }
2673 
2674     // 1 enable noice reduction
2675     // 0 disable noice reduction
processNoiceReductionEvent(int enable, BluetoothDevice device)2676     private void processNoiceReductionEvent(int enable, BluetoothDevice device) {
2677         HashMap<String, Integer> AudioParamNrec = mHeadsetAudioParam.get(device);
2678         if (AudioParamNrec != null && !AudioParamNrec.isEmpty()) {
2679             if (enable == 1)
2680                 AudioParamNrec.put("NREC", 1);
2681             else
2682                 AudioParamNrec.put("NREC", 0);
2683             log("NREC value for device :" + device + " is: " + AudioParamNrec.get("NREC"));
2684         } else {
2685             Log.e(TAG, "processNoiceReductionEvent: AudioParamNrec is null ");
2686         }
2687 
2688         if (mActiveScoDevice != null && mActiveScoDevice.equals(device)
2689                 && mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
2690             setAudioParameters(device);
2691         }
2692     }
2693 
2694     // 2 - WBS on
2695     // 1 - NBS on
processWBSEvent(int enable, BluetoothDevice device)2696     private void processWBSEvent(int enable, BluetoothDevice device) {
2697         if (enable == 2) {
2698             Log.d(TAG, "AudioManager.setParameters bt_wbs=on for " + device.getName() + " - "
2699                             + device.getAddress());
2700             mAudioManager.setParameters(HEADSET_WBS + "=on");
2701         } else {
2702             Log.d(TAG, "AudioManager.setParameters bt_wbs=off for " + device.getName() + " - "
2703                             + device.getAddress());
2704             mAudioManager.setParameters(HEADSET_WBS + "=off");
2705         }
2706     }
2707 
processAtChld(int chld, BluetoothDevice device)2708     private void processAtChld(int chld, BluetoothDevice device) {
2709         if (device == null) {
2710             Log.w(TAG, "processAtChld device is null");
2711             return;
2712         }
2713 
2714         if (mPhoneProxy != null) {
2715             try {
2716                 if (mPhoneProxy.processChld(chld)) {
2717                     atResponseCodeNative(
2718                             HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2719                 } else {
2720                     atResponseCodeNative(
2721                             HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2722                 }
2723             } catch (RemoteException e) {
2724                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
2725                 atResponseCodeNative(
2726                         HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2727             }
2728         } else {
2729             Log.e(TAG, "Handsfree phone proxy null for At+Chld");
2730             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2731         }
2732     }
2733 
processSubscriberNumberRequest(BluetoothDevice device)2734     private void processSubscriberNumberRequest(BluetoothDevice device) {
2735         if (device == null) {
2736             Log.w(TAG, "processSubscriberNumberRequest device is null");
2737             return;
2738         }
2739 
2740         if (mPhoneProxy != null) {
2741             try {
2742                 String number = mPhoneProxy.getSubscriberNumber();
2743                 if (number != null) {
2744                     atResponseStringNative("+CNUM: ,\"" + number + "\","
2745                                     + PhoneNumberUtils.toaFromString(number) + ",,4",
2746                             getByteAddress(device));
2747                     atResponseCodeNative(
2748                             HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2749                 } else {
2750                     Log.e(TAG, "getSubscriberNumber returns null");
2751                     atResponseCodeNative(
2752                             HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2753                 }
2754             } catch (RemoteException e) {
2755                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
2756                 atResponseCodeNative(
2757                         HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2758             }
2759         } else {
2760             Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
2761         }
2762     }
2763 
processAtCind(BluetoothDevice device)2764     private void processAtCind(BluetoothDevice device) {
2765         int call, call_setup;
2766 
2767         if (device == null) {
2768             Log.w(TAG, "processAtCind device is null");
2769             return;
2770         }
2771 
2772         /* Handsfree carkits expect that +CIND is properly responded to
2773          Hence we ensure that a proper response is sent
2774          for the virtual call too.*/
2775         if (isVirtualCallInProgress()) {
2776             call = 1;
2777             call_setup = 0;
2778         } else {
2779             // regular phone call
2780             call = mPhoneState.getNumActiveCall();
2781             call_setup = mPhoneState.getNumHeldCall();
2782         }
2783 
2784         cindResponseNative(mPhoneState.getService(), call, call_setup, mPhoneState.getCallState(),
2785                 mPhoneState.getSignal(), mPhoneState.getRoam(), mPhoneState.getBatteryCharge(),
2786                 getByteAddress(device));
2787     }
2788 
processAtCops(BluetoothDevice device)2789     private void processAtCops(BluetoothDevice device) {
2790         if (device == null) {
2791             Log.w(TAG, "processAtCops device is null");
2792             return;
2793         }
2794 
2795         if (mPhoneProxy != null) {
2796             try {
2797                 String operatorName = mPhoneProxy.getNetworkOperator();
2798                 if (operatorName == null) {
2799                     operatorName = "";
2800                 }
2801                 copsResponseNative(operatorName, getByteAddress(device));
2802             } catch (RemoteException e) {
2803                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
2804                 copsResponseNative("", getByteAddress(device));
2805             }
2806         } else {
2807             Log.e(TAG, "Handsfree phone proxy null for At+COPS");
2808             copsResponseNative("", getByteAddress(device));
2809         }
2810     }
2811 
processAtClcc(BluetoothDevice device)2812     private void processAtClcc(BluetoothDevice device) {
2813         if (device == null) {
2814             Log.w(TAG, "processAtClcc device is null");
2815             return;
2816         }
2817 
2818         if (mPhoneProxy != null) {
2819             try {
2820                 if (isVirtualCallInProgress()) {
2821                     String phoneNumber = "";
2822                     int type = PhoneNumberUtils.TOA_Unknown;
2823                     try {
2824                         phoneNumber = mPhoneProxy.getSubscriberNumber();
2825                         type = PhoneNumberUtils.toaFromString(phoneNumber);
2826                     } catch (RemoteException ee) {
2827                         Log.e(TAG, "Unable to retrieve phone number"
2828                                         + "using IBluetoothHeadsetPhone proxy");
2829                         phoneNumber = "";
2830                     }
2831                     clccResponseNative(
2832                             1, 0, 0, 0, false, phoneNumber, type, getByteAddress(device));
2833                     clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2834                 } else if (!mPhoneProxy.listCurrentCalls()) {
2835                     clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2836                 } else {
2837                     Log.d(TAG, "Starting CLCC response timeout for device: " + device);
2838                     Message m = obtainMessage(CLCC_RSP_TIMEOUT);
2839                     m.obj = getMatchingDevice(device);
2840                     sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE);
2841                 }
2842             } catch (RemoteException e) {
2843                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
2844                 clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2845             }
2846         } else {
2847             Log.e(TAG, "Handsfree phone proxy null for At+CLCC");
2848             clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2849         }
2850     }
2851 
processAtCscs(String atString, int type, BluetoothDevice device)2852     private void processAtCscs(String atString, int type, BluetoothDevice device) {
2853         log("processAtCscs - atString = " + atString);
2854         if (mPhonebook != null) {
2855             mPhonebook.handleCscsCommand(atString, type, device);
2856         } else {
2857             Log.e(TAG, "Phonebook handle null for At+CSCS");
2858             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2859         }
2860     }
2861 
processAtCpbs(String atString, int type, BluetoothDevice device)2862     private void processAtCpbs(String atString, int type, BluetoothDevice device) {
2863         log("processAtCpbs - atString = " + atString);
2864         if (mPhonebook != null) {
2865             mPhonebook.handleCpbsCommand(atString, type, device);
2866         } else {
2867             Log.e(TAG, "Phonebook handle null for At+CPBS");
2868             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2869         }
2870     }
2871 
processAtCpbr(String atString, int type, BluetoothDevice device)2872     private void processAtCpbr(String atString, int type, BluetoothDevice device) {
2873         log("processAtCpbr - atString = " + atString);
2874         if (mPhonebook != null) {
2875             mPhonebook.handleCpbrCommand(atString, type, device);
2876         } else {
2877             Log.e(TAG, "Phonebook handle null for At+CPBR");
2878             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2879         }
2880     }
2881 
2882     /**
2883      * Find a character ch, ignoring quoted sections.
2884      * Return input.length() if not found.
2885      */
findChar(char ch, String input, int fromIndex)2886     static private int findChar(char ch, String input, int fromIndex) {
2887         for (int i = fromIndex; i < input.length(); i++) {
2888             char c = input.charAt(i);
2889             if (c == '"') {
2890                 i = input.indexOf('"', i + 1);
2891                 if (i == -1) {
2892                     return input.length();
2893                 }
2894             } else if (c == ch) {
2895                 return i;
2896             }
2897         }
2898         return input.length();
2899     }
2900 
2901     /**
2902      * Break an argument string into individual arguments (comma delimited).
2903      * Integer arguments are turned into Integer objects. Otherwise a String
2904      * object is used.
2905      */
generateArgs(String input)2906     static private Object[] generateArgs(String input) {
2907         int i = 0;
2908         int j;
2909         ArrayList<Object> out = new ArrayList<Object>();
2910         while (i <= input.length()) {
2911             j = findChar(',', input, i);
2912 
2913             String arg = input.substring(i, j);
2914             try {
2915                 out.add(new Integer(arg));
2916             } catch (NumberFormatException e) {
2917                 out.add(arg);
2918             }
2919 
2920             i = j + 1; // move past comma
2921         }
2922         return out.toArray();
2923     }
2924 
2925     /**
2926      * @return {@code true} if the given string is a valid vendor-specific AT command.
2927      */
processVendorSpecificAt(String atString)2928     private boolean processVendorSpecificAt(String atString) {
2929         log("processVendorSpecificAt - atString = " + atString);
2930 
2931         // Currently we accept only SET type commands.
2932         int indexOfEqual = atString.indexOf("=");
2933         if (indexOfEqual == -1) {
2934             Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2935             return false;
2936         }
2937 
2938         String command = atString.substring(0, indexOfEqual);
2939         Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
2940         if (companyId == null) {
2941             Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString);
2942             return false;
2943         }
2944 
2945         String arg = atString.substring(indexOfEqual + 1);
2946         if (arg.startsWith("?")) {
2947             Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2948             return false;
2949         }
2950 
2951         Object[] args = generateArgs(arg);
2952         broadcastVendorSpecificEventIntent(
2953                 command, companyId, BluetoothHeadset.AT_CMD_TYPE_SET, args, mCurrentDevice);
2954         atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(mCurrentDevice));
2955         return true;
2956     }
2957 
processUnknownAt(String atString, BluetoothDevice device)2958     private void processUnknownAt(String atString, BluetoothDevice device) {
2959         if (device == null) {
2960             Log.w(TAG, "processUnknownAt device is null");
2961             return;
2962         }
2963 
2964         // TODO (BT)
2965         log("processUnknownAt - atString = " + atString);
2966         String atCommand = parseUnknownAt(atString);
2967         int commandType = getAtCommandType(atCommand);
2968         if (atCommand.startsWith("+CSCS"))
2969             processAtCscs(atCommand.substring(5), commandType, device);
2970         else if (atCommand.startsWith("+CPBS"))
2971             processAtCpbs(atCommand.substring(5), commandType, device);
2972         else if (atCommand.startsWith("+CPBR"))
2973             processAtCpbr(atCommand.substring(5), commandType, device);
2974         else if (!processVendorSpecificAt(atCommand))
2975             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2976     }
2977 
processKeyPressed(BluetoothDevice device)2978     private void processKeyPressed(BluetoothDevice device) {
2979         if (device == null) {
2980             Log.w(TAG, "processKeyPressed device is null");
2981             return;
2982         }
2983 
2984         if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
2985             if (mPhoneProxy != null) {
2986                 try {
2987                     mPhoneProxy.answerCall();
2988                 } catch (RemoteException e) {
2989                     Log.e(TAG, Log.getStackTraceString(new Throwable()));
2990                 }
2991             } else {
2992                 Log.e(TAG, "Handsfree phone proxy null for answering call");
2993             }
2994         } else if (mPhoneState.getNumActiveCall() > 0) {
2995             if (!isAudioOn()) {
2996                 connectAudioNative(getByteAddress(mCurrentDevice));
2997             } else {
2998                 if (mPhoneProxy != null) {
2999                     try {
3000                         mPhoneProxy.hangupCall();
3001                     } catch (RemoteException e) {
3002                         Log.e(TAG, Log.getStackTraceString(new Throwable()));
3003                     }
3004                 } else {
3005                     Log.e(TAG, "Handsfree phone proxy null for hangup call");
3006                 }
3007             }
3008         } else {
3009             String dialNumber = mPhonebook.getLastDialledNumber();
3010             if (dialNumber == null) {
3011                 if (DBG) log("processKeyPressed, last dial number null");
3012                 return;
3013             }
3014             Intent intent = new Intent(
3015                     Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null));
3016             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3017             mService.startActivity(intent);
3018         }
3019     }
3020 
sendIndicatorIntent(BluetoothDevice device, int ind_id, String ind_value)3021     private void sendIndicatorIntent(BluetoothDevice device, int ind_id, String ind_value) {
3022         Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
3023         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
3024         intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, ind_id);
3025         if (ind_value != null)
3026             intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, ind_value);
3027 
3028         mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
3029     }
3030 
processAtBind(String at_string, BluetoothDevice device)3031     private void processAtBind(String at_string, BluetoothDevice device) {
3032         log("processAtBind processAtBind: " + at_string);
3033 
3034         // Parse the AT String to find the Indicator Ids that are supported
3035         int ind_id = 0;
3036         int iter = 0;
3037         int iter1 = 0;
3038 
3039         while (iter < at_string.length()) {
3040             iter1 = findChar(',', at_string, iter);
3041             String id = at_string.substring(iter, iter1);
3042 
3043             try {
3044                 ind_id = new Integer(id);
3045             } catch (NumberFormatException e) {
3046                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
3047             }
3048 
3049             switch (ind_id) {
3050                 case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY:
3051                     log("Send Broadcast intent for the"
3052                             + "Enhanced Driver Safety indicator.");
3053                     sendIndicatorIntent(device, ind_id, null);
3054                     break;
3055                 case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS:
3056                     log("Send Broadcast intent for the"
3057                             + "Battery Level indicator.");
3058                     sendIndicatorIntent(device, ind_id, null);
3059                     break;
3060                 default:
3061                     log("Invalid HF Indicator Received");
3062                     break;
3063             }
3064 
3065             iter = iter1 + 1; // move past comma
3066         }
3067     }
3068 
processAtBiev(int ind_id, int ind_value, BluetoothDevice device)3069     private void processAtBiev(int ind_id, int ind_value, BluetoothDevice device) {
3070         log(" Process AT + BIEV Command : " + ind_id + ", " + ind_value);
3071 
3072         String ind_value_str = Integer.toString(ind_value);
3073 
3074         Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
3075         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
3076         sendIndicatorIntent(device, ind_id, ind_value_str);
3077     }
3078 
onConnectionStateChanged(int state, byte[] address)3079     private void onConnectionStateChanged(int state, byte[] address) {
3080         StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
3081         event.valueInt = state;
3082         event.device = getDevice(address);
3083         sendMessage(STACK_EVENT, event);
3084     }
3085 
onAudioStateChanged(int state, byte[] address)3086     private void onAudioStateChanged(int state, byte[] address) {
3087         StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
3088         event.valueInt = state;
3089         event.device = getDevice(address);
3090         sendMessage(STACK_EVENT, event);
3091     }
3092 
onVrStateChanged(int state, byte[] address)3093     private void onVrStateChanged(int state, byte[] address) {
3094         StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
3095         event.valueInt = state;
3096         event.device = getDevice(address);
3097         sendMessage(STACK_EVENT, event);
3098     }
3099 
onAnswerCall(byte[] address)3100     private void onAnswerCall(byte[] address) {
3101         StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL);
3102         event.device = getDevice(address);
3103         sendMessage(STACK_EVENT, event);
3104     }
3105 
onHangupCall(byte[] address)3106     private void onHangupCall(byte[] address) {
3107         StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);
3108         event.device = getDevice(address);
3109         sendMessage(STACK_EVENT, event);
3110     }
3111 
onVolumeChanged(int type, int volume, byte[] address)3112     private void onVolumeChanged(int type, int volume, byte[] address) {
3113         StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
3114         event.valueInt = type;
3115         event.valueInt2 = volume;
3116         event.device = getDevice(address);
3117         sendMessage(STACK_EVENT, event);
3118     }
3119 
onDialCall(String number, byte[] address)3120     private void onDialCall(String number, byte[] address) {
3121         StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);
3122         event.valueString = number;
3123         event.device = getDevice(address);
3124         sendMessage(STACK_EVENT, event);
3125     }
3126 
onSendDtmf(int dtmf, byte[] address)3127     private void onSendDtmf(int dtmf, byte[] address) {
3128         StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF);
3129         event.valueInt = dtmf;
3130         event.device = getDevice(address);
3131         sendMessage(STACK_EVENT, event);
3132     }
3133 
onNoiceReductionEnable(boolean enable, byte[] address)3134     private void onNoiceReductionEnable(boolean enable, byte[] address) {
3135         StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION);
3136         event.valueInt = enable ? 1 : 0;
3137         event.device = getDevice(address);
3138         sendMessage(STACK_EVENT, event);
3139     }
3140 
onWBS(int codec, byte[] address)3141     private void onWBS(int codec, byte[] address) {
3142         StackEvent event = new StackEvent(EVENT_TYPE_WBS);
3143         event.valueInt = codec;
3144         event.device = getDevice(address);
3145         sendMessage(STACK_EVENT, event);
3146     }
3147 
onAtChld(int chld, byte[] address)3148     private void onAtChld(int chld, byte[] address) {
3149         StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD);
3150         event.valueInt = chld;
3151         event.device = getDevice(address);
3152         sendMessage(STACK_EVENT, event);
3153     }
3154 
onAtCnum(byte[] address)3155     private void onAtCnum(byte[] address) {
3156         StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST);
3157         event.device = getDevice(address);
3158         sendMessage(STACK_EVENT, event);
3159     }
3160 
onAtCind(byte[] address)3161     private void onAtCind(byte[] address) {
3162         StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND);
3163         event.device = getDevice(address);
3164         sendMessage(STACK_EVENT, event);
3165     }
3166 
onAtCops(byte[] address)3167     private void onAtCops(byte[] address) {
3168         StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS);
3169         event.device = getDevice(address);
3170         sendMessage(STACK_EVENT, event);
3171     }
3172 
onAtClcc(byte[] address)3173     private void onAtClcc(byte[] address) {
3174         StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC);
3175         event.device = getDevice(address);
3176         sendMessage(STACK_EVENT, event);
3177     }
3178 
onUnknownAt(String atString, byte[] address)3179     private void onUnknownAt(String atString, byte[] address) {
3180         StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT);
3181         event.valueString = atString;
3182         event.device = getDevice(address);
3183         sendMessage(STACK_EVENT, event);
3184     }
3185 
onKeyPressed(byte[] address)3186     private void onKeyPressed(byte[] address) {
3187         StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED);
3188         event.device = getDevice(address);
3189         sendMessage(STACK_EVENT, event);
3190     }
3191 
onATBind(String atString, byte[] address)3192     private void onATBind(String atString, byte[] address) {
3193         StackEvent event = new StackEvent(EVENT_TYPE_BIND);
3194         event.valueString = atString;
3195         event.device = getDevice(address);
3196         sendMessage(STACK_EVENT, event);
3197     }
3198 
onATBiev(int ind_id, int ind_value, byte[] address)3199     private void onATBiev(int ind_id, int ind_value, byte[] address) {
3200         StackEvent event = new StackEvent(EVENT_TYPE_BIEV);
3201         event.valueInt = ind_id;
3202         event.valueInt2 = ind_value;
3203         event.device = getDevice(address);
3204         sendMessage(STACK_EVENT, event);
3205     }
3206 
processIntentBatteryChanged(Intent intent)3207     private void processIntentBatteryChanged(Intent intent) {
3208         int batteryLevel = intent.getIntExtra("level", -1);
3209         int scale = intent.getIntExtra("scale", -1);
3210         if (batteryLevel == -1 || scale == -1 || scale == 0) {
3211             Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale);
3212             return;
3213         }
3214         batteryLevel = batteryLevel * 5 / scale;
3215         mPhoneState.setBatteryCharge(batteryLevel);
3216     }
3217 
processDeviceStateChanged(HeadsetDeviceState deviceState)3218     private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
3219         notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal,
3220                 deviceState.mBatteryCharge);
3221     }
3222 
processSendClccResponse(HeadsetClccResponse clcc)3223     private void processSendClccResponse(HeadsetClccResponse clcc) {
3224         BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT);
3225         if (device == null) {
3226             return;
3227         }
3228         if (clcc.mIndex == 0) {
3229             removeMessages(CLCC_RSP_TIMEOUT);
3230         }
3231         clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty,
3232                 clcc.mNumber, clcc.mType, getByteAddress(device));
3233     }
3234 
processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode)3235     private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
3236         String stringToSend = resultCode.mCommand + ": ";
3237         if (resultCode.mArg != null) {
3238             stringToSend += resultCode.mArg;
3239         }
3240         atResponseStringNative(stringToSend, getByteAddress(resultCode.mDevice));
3241     }
3242 
getCurrentDeviceName(BluetoothDevice device)3243     private String getCurrentDeviceName(BluetoothDevice device) {
3244         String defaultName = "<unknown>";
3245 
3246         if (device == null) {
3247             return defaultName;
3248         }
3249 
3250         String deviceName = device.getName();
3251         if (deviceName == null) {
3252             return defaultName;
3253         }
3254         return deviceName;
3255     }
3256 
getByteAddress(BluetoothDevice device)3257     private byte[] getByteAddress(BluetoothDevice device) {
3258         return Utils.getBytesFromAddress(device.getAddress());
3259     }
3260 
getDevice(byte[] address)3261     private BluetoothDevice getDevice(byte[] address) {
3262         return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
3263     }
3264 
isInCall()3265     private boolean isInCall() {
3266         return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0)
3267                 || ((mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE)
3268                            && (mPhoneState.getCallState()
3269                                       != HeadsetHalConstants.CALL_STATE_INCOMING)));
3270     }
3271 
isRinging()3272     private boolean isRinging() {
3273         return mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING;
3274     }
3275 
3276     // Accept incoming SCO only when there is in-band ringing, incoming call,
3277     // active call, VR activated, active VOIP call
isScoAcceptable()3278     private boolean isScoAcceptable() {
3279         return mAudioRouteAllowed
3280                 && (mVoiceRecognitionStarted || isInCall()
3281                            || (BluetoothHeadset.isInbandRingingSupported(mService) && isRinging()));
3282     }
3283 
isConnected()3284     boolean isConnected() {
3285         IState currentState = getCurrentState();
3286         return (currentState == mConnected || currentState == mAudioOn);
3287     }
3288 
okToConnect(BluetoothDevice device)3289     boolean okToConnect(BluetoothDevice device) {
3290         AdapterService adapterService = AdapterService.getAdapterService();
3291         int priority = mService.getPriority(device);
3292         boolean ret = false;
3293         // check if this is an incoming connection in Quiet mode.
3294         if ((adapterService == null)
3295                 || ((adapterService.isQuietModeEnabled() == true) && (mTargetDevice == null))) {
3296             ret = false;
3297         }
3298         // check priority and accept or reject the connection. if priority is undefined
3299         // it is likely that our SDP has not completed and peer is initiating the
3300         // connection. Allow this connection, provided the device is bonded
3301         else if ((BluetoothProfile.PRIORITY_OFF < priority)
3302                 || ((BluetoothProfile.PRIORITY_UNDEFINED == priority)
3303                            && (device.getBondState() != BluetoothDevice.BOND_NONE))) {
3304             ret = true;
3305         }
3306         return ret;
3307     }
3308 
3309     @Override
log(String msg)3310     protected void log(String msg) {
3311         if (DBG) {
3312             super.log(msg);
3313         }
3314     }
3315 
handleAccessPermissionResult(Intent intent)3316     public void handleAccessPermissionResult(Intent intent) {
3317         log("handleAccessPermissionResult");
3318         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3319         if (mPhonebook != null) {
3320             if (!mPhonebook.getCheckingAccessPermission()) {
3321                 return;
3322             }
3323             int atCommandResult = 0;
3324             int atCommandErrorCode = 0;
3325             // HeadsetBase headset = mHandsfree.getHeadset();
3326             // ASSERT: (headset != null) && headSet.isConnected()
3327             // REASON: mCheckingAccessPermission is true, otherwise resetAtState
3328             // has set mCheckingAccessPermission to false
3329             if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
3330                 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
3331                             BluetoothDevice.CONNECTION_ACCESS_NO)
3332                         == BluetoothDevice.CONNECTION_ACCESS_YES) {
3333                     if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
3334                         mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
3335                     }
3336                     atCommandResult = mPhonebook.processCpbrCommand(device);
3337                 } else {
3338                     if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
3339                         mCurrentDevice.setPhonebookAccessPermission(
3340                                 BluetoothDevice.ACCESS_REJECTED);
3341                     }
3342                 }
3343             }
3344             mPhonebook.setCpbrIndex(-1);
3345             mPhonebook.setCheckingAccessPermission(false);
3346 
3347             if (atCommandResult >= 0) {
3348                 atResponseCodeNative(atCommandResult, atCommandErrorCode, getByteAddress(device));
3349             } else {
3350                 log("handleAccessPermissionResult - RESULT_NONE");
3351             }
3352         } else {
3353             Log.e(TAG, "Phonebook handle null");
3354             if (device != null) {
3355                 atResponseCodeNative(
3356                         HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
3357             }
3358         }
3359     }
3360 
3361     private static final String SCHEME_TEL = "tel";
3362 
3363     // Event types for STACK_EVENT message
3364     final private static int EVENT_TYPE_NONE = 0;
3365     final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
3366     final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
3367     final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
3368     final private static int EVENT_TYPE_ANSWER_CALL = 4;
3369     final private static int EVENT_TYPE_HANGUP_CALL = 5;
3370     final private static int EVENT_TYPE_VOLUME_CHANGED = 6;
3371     final private static int EVENT_TYPE_DIAL_CALL = 7;
3372     final private static int EVENT_TYPE_SEND_DTMF = 8;
3373     final private static int EVENT_TYPE_NOICE_REDUCTION = 9;
3374     final private static int EVENT_TYPE_AT_CHLD = 10;
3375     final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11;
3376     final private static int EVENT_TYPE_AT_CIND = 12;
3377     final private static int EVENT_TYPE_AT_COPS = 13;
3378     final private static int EVENT_TYPE_AT_CLCC = 14;
3379     final private static int EVENT_TYPE_UNKNOWN_AT = 15;
3380     final private static int EVENT_TYPE_KEY_PRESSED = 16;
3381     final private static int EVENT_TYPE_WBS = 17;
3382     final private static int EVENT_TYPE_BIND = 18;
3383     final private static int EVENT_TYPE_BIEV = 19;
3384 
3385     private class StackEvent {
3386         int type = EVENT_TYPE_NONE;
3387         int valueInt = 0;
3388         int valueInt2 = 0;
3389         String valueString = null;
3390         BluetoothDevice device = null;
3391 
StackEvent(int type)3392         private StackEvent(int type) {
3393             this.type = type;
3394         }
3395     }
3396 
atResponseCodeNative( int responseCode, int errorCode, byte[] address)3397     /*package*/ native boolean atResponseCodeNative(
3398             int responseCode, int errorCode, byte[] address);
atResponseStringNative(String responseString, byte[] address)3399     /*package*/ native boolean atResponseStringNative(String responseString, byte[] address);
3400 
classInitNative()3401     private native static void classInitNative();
initializeNative(int max_hf_clients, boolean inband_ring_enable)3402     private native void initializeNative(int max_hf_clients, boolean inband_ring_enable);
cleanupNative()3403     private native void cleanupNative();
connectHfpNative(byte[] address)3404     private native boolean connectHfpNative(byte[] address);
disconnectHfpNative(byte[] address)3405     private native boolean disconnectHfpNative(byte[] address);
connectAudioNative(byte[] address)3406     private native boolean connectAudioNative(byte[] address);
disconnectAudioNative(byte[] address)3407     private native boolean disconnectAudioNative(byte[] address);
startVoiceRecognitionNative(byte[] address)3408     private native boolean startVoiceRecognitionNative(byte[] address);
stopVoiceRecognitionNative(byte[] address)3409     private native boolean stopVoiceRecognitionNative(byte[] address);
setVolumeNative(int volumeType, int volume, byte[] address)3410     private native boolean setVolumeNative(int volumeType, int volume, byte[] address);
cindResponseNative(int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge, byte[] address)3411     private native boolean cindResponseNative(int service, int numActive, int numHeld,
3412             int callState, int signal, int roam, int batteryCharge, byte[] address);
bindResponseNative(int ind_id, boolean ind_status, byte[] address)3413     private native boolean bindResponseNative(int ind_id, boolean ind_status, byte[] address);
notifyDeviceStatusNative( int networkState, int serviceType, int signal, int batteryCharge)3414     private native boolean notifyDeviceStatusNative(
3415             int networkState, int serviceType, int signal, int batteryCharge);
3416 
clccResponseNative(int index, int dir, int status, int mode, boolean mpty, String number, int type, byte[] address)3417     private native boolean clccResponseNative(int index, int dir, int status, int mode,
3418             boolean mpty, String number, int type, byte[] address);
copsResponseNative(String operatorName, byte[] address)3419     private native boolean copsResponseNative(String operatorName, byte[] address);
3420 
phoneStateChangeNative( int numActive, int numHeld, int callState, String number, int type)3421     private native boolean phoneStateChangeNative(
3422             int numActive, int numHeld, int callState, String number, int type);
configureWBSNative(byte[] address, int condec_config)3423     private native boolean configureWBSNative(byte[] address, int condec_config);
3424 }
3425