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