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