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