HeadsetClientStateMachine.java revision 525bb0caa0d8f4a8093890cab4bc058ef909a637
1/* 2 * Copyright (c) 2014 The Android Open Source Project 3 * Copyright (C) 2012 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18/** 19 * Bluetooth Headset Client StateMachine 20 * (Disconnected) 21 * | ^ ^ 22 * CONNECT | | | DISCONNECTED 23 * V | | 24 * (Connecting) | 25 * | | 26 * CONNECTED | | DISCONNECT 27 * V | 28 * (Connected) 29 * | ^ 30 * CONNECT_AUDIO | | DISCONNECT_AUDIO 31 * V | 32 * (AudioOn) 33 */ 34 35package com.android.bluetooth.hfpclient; 36 37import android.bluetooth.BluetoothAdapter; 38import android.bluetooth.BluetoothDevice; 39import android.bluetooth.BluetoothHeadsetClient; 40import android.bluetooth.BluetoothHeadsetClientCall; 41import android.bluetooth.BluetoothProfile; 42import android.bluetooth.BluetoothUuid; 43import android.content.Context; 44import android.content.Intent; 45import android.media.AudioManager; 46import android.os.Bundle; 47import android.os.Message; 48import android.os.ParcelUuid; 49import android.os.SystemClock; 50import android.util.Log; 51import android.util.Pair; 52import android.telecom.TelecomManager; 53 54import com.android.bluetooth.Utils; 55import com.android.bluetooth.btservice.AdapterService; 56import com.android.bluetooth.btservice.ProfileService; 57import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService; 58import com.android.internal.util.IState; 59import com.android.internal.util.State; 60import com.android.internal.util.StateMachine; 61 62import java.util.ArrayList; 63import java.util.Arrays; 64import java.util.HashSet; 65import java.util.Hashtable; 66import java.util.Iterator; 67import java.util.LinkedList; 68import java.util.List; 69import java.util.Queue; 70import java.util.Set; 71 72import com.android.bluetooth.R; 73 74final class HeadsetClientStateMachine extends StateMachine { 75 private static final String TAG = "HeadsetClientStateMachine"; 76 private static final boolean DBG = true; 77 78 static final int NO_ACTION = 0; 79 80 // external actions 81 public static final int CONNECT = 1; 82 public static final int DISCONNECT = 2; 83 public static final int CONNECT_AUDIO = 3; 84 public static final int DISCONNECT_AUDIO = 4; 85 public static final int VOICE_RECOGNITION_START = 5; 86 public static final int VOICE_RECOGNITION_STOP = 6; 87 public static final int SET_MIC_VOLUME = 7; 88 public static final int SET_SPEAKER_VOLUME = 8; 89 public static final int DIAL_NUMBER = 10; 90 public static final int ACCEPT_CALL = 12; 91 public static final int REJECT_CALL = 13; 92 public static final int HOLD_CALL = 14; 93 public static final int TERMINATE_CALL = 15; 94 public static final int ENTER_PRIVATE_MODE = 16; 95 public static final int SEND_DTMF = 17; 96 public static final int EXPLICIT_CALL_TRANSFER = 18; 97 public static final int LAST_VTAG_NUMBER = 19; 98 public static final int DISABLE_NREC = 20; 99 100 // internal actions 101 private static final int QUERY_CURRENT_CALLS = 50; 102 private static final int QUERY_OPERATOR_NAME = 51; 103 private static final int SUBSCRIBER_INFO = 52; 104 private static final int CONNECTING_TIMEOUT = 53; 105 106 // special action to handle terminating specific call from multiparty call 107 static final int TERMINATE_SPECIFIC_CALL = 53; 108 109 // Timeouts. 110 static final int CONNECTING_TIMEOUT_MS = 5000; 111 112 static final int MAX_HFP_SCO_VOICE_CALL_VOLUME = 15; // HFP 1.5 spec. 113 static final int MIN_HFP_SCO_VOICE_CALL_VOLUME = 1; // HFP 1.5 spec. 114 115 private static final int STACK_EVENT = 100; 116 117 public static final Integer HF_ORIGINATED_CALL_ID = new Integer(-1); 118 private long OUTGOING_TIMEOUT_MILLI = 10 * 1000; // 10 seconds 119 private long QUERY_CURRENT_CALLS_WAIT_MILLIS = 2 * 1000; // 2 seconds 120 121 private final Disconnected mDisconnected; 122 private final Connecting mConnecting; 123 private final Connected mConnected; 124 private final AudioOn mAudioOn; 125 private long mClccTimer = 0; 126 127 private final HeadsetClientService mService; 128 129 // Set of calls that represent the accurate state of calls that exists on AG and the calls that 130 // are currently in process of being notified to the AG from HF. 131 private final Hashtable<Integer, BluetoothHeadsetClientCall> mCalls = new Hashtable<>(); 132 // Set of calls received from AG via the AT+CLCC command. We use this map to update the mCalls 133 // which is eventually used to inform the telephony stack of any changes to call on HF. 134 private final Hashtable<Integer, BluetoothHeadsetClientCall> mCallsUpdate = new Hashtable<>(); 135 136 private int mIndicatorNetworkState; 137 private int mIndicatorNetworkType; 138 private int mIndicatorNetworkSignal; 139 private int mIndicatorBatteryLevel; 140 141 private int mIndicatorCall; 142 private int mIndicatorCallSetup; 143 private int mIndicatorCallHeld; 144 private boolean mVgsFromStack = false; 145 private boolean mVgmFromStack = false; 146 147 private String mOperatorName; 148 private String mSubscriberInfo; 149 150 private int mVoiceRecognitionActive; 151 private int mInBandRingtone; 152 153 private int mMaxAmVcVol; 154 private int mMinAmVcVol; 155 156 // queue of send actions (pair action, action_data) 157 private Queue<Pair<Integer, Object>> mQueuedActions; 158 159 // last executed command, before action is complete e.g. waiting for some 160 // indicator 161 private Pair<Integer, Object> mPendingAction; 162 163 private final AudioManager mAudioManager; 164 private int mAudioState; 165 // Indicates whether audio can be routed to the device. 166 private boolean mAudioRouteAllowed; 167 private boolean mAudioWbs; 168 private final BluetoothAdapter mAdapter; 169 private boolean mNativeAvailable; 170 private TelecomManager mTelecomManager; 171 172 // currently connected device 173 private BluetoothDevice mCurrentDevice = null; 174 175 // general peer features and call handling features 176 private int mPeerFeatures; 177 private int mChldFeatures; 178 179 static { 180 classInitNative(); 181 } 182 183 public void dump(StringBuilder sb) { 184 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 185 ProfileService.println(sb, "mAudioOn: " + mAudioOn); 186 ProfileService.println(sb, "mAudioState: " + mAudioState); 187 ProfileService.println(sb, "mAudioWbs: " + mAudioWbs); 188 ProfileService.println(sb, "mIndicatorNetworkState: " + mIndicatorNetworkState); 189 ProfileService.println(sb, "mIndicatorNetworkType: " + mIndicatorNetworkType); 190 ProfileService.println(sb, "mIndicatorNetworkSignal: " + mIndicatorNetworkSignal); 191 ProfileService.println(sb, "mIndicatorBatteryLevel: " + mIndicatorBatteryLevel); 192 ProfileService.println(sb, "mIndicatorCall: " + mIndicatorCall); 193 ProfileService.println(sb, "mIndicatorCallSetup: " + mIndicatorCallSetup); 194 ProfileService.println(sb, "mIndicatorCallHeld: " + mIndicatorCallHeld); 195 ProfileService.println(sb, "mVgsFromStack: " + mVgsFromStack); 196 ProfileService.println(sb, "mVgmFromStack: " + mVgmFromStack); 197 ProfileService.println(sb, "mOperatorName: " + mOperatorName); 198 ProfileService.println(sb, "mSubscriberInfo: " + mSubscriberInfo); 199 ProfileService.println(sb, "mVoiceRecognitionActive: " + mVoiceRecognitionActive); 200 ProfileService.println(sb, "mInBandRingtone: " + mInBandRingtone); 201 202 ProfileService.println(sb, "mCalls:"); 203 if (mCalls != null) { 204 for (BluetoothHeadsetClientCall call : mCalls.values()) { 205 ProfileService.println(sb, " " + call); 206 } 207 } 208 209 ProfileService.println(sb, "mCallsUpdate:"); 210 if (mCallsUpdate != null) { 211 for (BluetoothHeadsetClientCall call : mCallsUpdate.values()) { 212 ProfileService.println(sb, " " + call); 213 } 214 } 215 } 216 217 private void clearPendingAction() { 218 mPendingAction = new Pair<Integer, Object>(NO_ACTION, 0); 219 } 220 221 private void addQueuedAction(int action) { 222 addQueuedAction(action, 0); 223 } 224 225 private void addQueuedAction(int action, Object data) { 226 mQueuedActions.add(new Pair<Integer, Object>(action, data)); 227 } 228 229 private void addQueuedAction(int action, int data) { 230 mQueuedActions.add(new Pair<Integer, Object>(action, data)); 231 } 232 233 private BluetoothHeadsetClientCall getCall(int... states) { 234 if (DBG) { 235 Log.d(TAG, "getFromCallsWithStates states:" + Arrays.toString(states)); 236 } 237 for (BluetoothHeadsetClientCall c : mCalls.values()) { 238 for (int s : states) { 239 if (c.getState() == s) { 240 return c; 241 } 242 } 243 } 244 return null; 245 } 246 247 private int callsInState(int state) { 248 int i = 0; 249 for (BluetoothHeadsetClientCall c : mCalls.values()) { 250 if (c.getState() == state) { 251 i++; 252 } 253 } 254 255 return i; 256 } 257 258 private void updateCallsMultiParty() { 259 boolean multi = callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) > 1; 260 261 for (BluetoothHeadsetClientCall c : mCalls.values()) { 262 if (c.getState() == BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) { 263 if (c.isMultiParty() == multi) { 264 continue; 265 } 266 267 c.setMultiParty(multi); 268 sendCallChangedIntent(c); 269 } else { 270 if (c.isMultiParty()) { 271 c.setMultiParty(false); 272 sendCallChangedIntent(c); 273 } 274 } 275 } 276 } 277 278 private void setCallState(BluetoothHeadsetClientCall c, int state) { 279 if (state == c.getState()) { 280 return; 281 } 282 c.setState(state); 283 sendCallChangedIntent(c); 284 } 285 286 private void sendCallChangedIntent(BluetoothHeadsetClientCall c) { 287 if (DBG) { 288 Log.d(TAG, "sendCallChangedIntent " + c); 289 } 290 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED); 291 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 292 intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c); 293 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 294 } 295 296 private void updateCallIndicator(int call) { 297 if (DBG) { 298 Log.d(TAG, "updateCallIndicator " + call); 299 } 300 mIndicatorCall = call; 301 sendMessage(QUERY_CURRENT_CALLS); 302 } 303 304 private void updateCallSetupIndicator(int callSetup) { 305 if (DBG) { 306 Log.d(TAG, "updateCallSetupIndicator " + callSetup + " " + mPendingAction.first); 307 } 308 mIndicatorCallSetup = callSetup; 309 sendMessage(QUERY_CURRENT_CALLS); 310 } 311 312 private void updateCallHeldIndicator(int callHeld) { 313 if (DBG) { 314 Log.d(TAG, "updateCallHeld " + callHeld); 315 } 316 mIndicatorCallHeld = callHeld; 317 sendMessage(QUERY_CURRENT_CALLS); 318 } 319 320 private void updateRespAndHold(int resp_and_hold) { 321 if (DBG) { 322 Log.d(TAG, "updatRespAndHold " + resp_and_hold); 323 } 324 sendMessage(QUERY_CURRENT_CALLS); 325 } 326 327 private void updateClip(String number) { 328 if (DBG) { 329 Log.d(TAG, "updateClip " + number); 330 } 331 sendMessage(QUERY_CURRENT_CALLS); 332 } 333 334 private void updateCCWA(String number) { 335 if (DBG) { 336 Log.d(TAG, "updateCCWA " + number); 337 } 338 sendMessage(QUERY_CURRENT_CALLS); 339 } 340 341 private boolean queryCallsStart() { 342 if (DBG) { 343 Log.d(TAG, "queryCallsStart"); 344 } 345 clearPendingAction(); 346 queryCurrentCallsNative(); 347 addQueuedAction(QUERY_CURRENT_CALLS, 0); 348 return true; 349 } 350 351 private void queryCallsDone() { 352 if (DBG) { 353 Log.d(TAG, "queryCallsDone"); 354 } 355 Iterator<Hashtable.Entry<Integer, BluetoothHeadsetClientCall>> it; 356 357 // mCalls has two types of calls: 358 // (a) Calls that are received from AG of a previous iteration of queryCallsStart() 359 // (b) Calls that are outgoing initiated from HF 360 // mCallsUpdate has all calls received from queryCallsUpdate() in current iteration of 361 // queryCallsStart(). 362 // 363 // We use the following steps to make sure that calls are update correctly. 364 // 365 // If there are no calls initiated from HF (i.e. ID = -1) then: 366 // 1. All IDs which are common in mCalls & mCallsUpdate are updated and the upper layers are 367 // informed of the change calls (if any changes). 368 // 2. All IDs that are in mCalls but *not* in mCallsUpdate will be removed from mCalls and 369 // the calls should be terminated 370 // 3. All IDs that are new in mCallsUpdated should be added as new calls to mCalls. 371 // 372 // If there is an outgoing HF call, it is important to associate that call with one of the 373 // mCallsUpdated calls hence, 374 // 1. If from the above procedure we get N extra calls (i.e. {3}): 375 // choose the first call as the one to associate with the HF call. 376 377 // Create set of IDs for added calls, removed calls and consitent calls. 378 // WARN!!! Java Map -> Set has association hence changes to Set are reflected in the Map 379 // itself (i.e. removing an element from Set removes it from the Map hence use copy). 380 Set<Integer> currCallIdSet = new HashSet<Integer>(); 381 currCallIdSet.addAll(mCalls.keySet()); 382 // Remove the entry for unassigned call. 383 currCallIdSet.remove(HF_ORIGINATED_CALL_ID); 384 385 Set<Integer> newCallIdSet = new HashSet<Integer>(); 386 newCallIdSet.addAll(mCallsUpdate.keySet()); 387 388 // Added. 389 Set<Integer> callAddedIds = new HashSet<Integer>(); 390 callAddedIds.addAll(newCallIdSet); 391 callAddedIds.removeAll(currCallIdSet); 392 393 // Removed. 394 Set<Integer> callRemovedIds = new HashSet<Integer>(); 395 callRemovedIds.addAll(currCallIdSet); 396 callRemovedIds.removeAll(newCallIdSet); 397 398 // Retained. 399 Set<Integer> callRetainedIds = new HashSet<Integer>(); 400 callRetainedIds.addAll(currCallIdSet); 401 callRetainedIds.retainAll(newCallIdSet); 402 403 if (DBG) { 404 Log.d(TAG, "currCallIdSet " + mCalls.keySet() + " newCallIdSet " + newCallIdSet + 405 " callAddedIds " + callAddedIds + " callRemovedIds " + callRemovedIds + 406 " callRetainedIds " + callRetainedIds); 407 } 408 409 // First thing is to try to associate the outgoing HF with a valid call. 410 Integer hfOriginatedAssoc = -1; 411 if (mCalls.containsKey(HF_ORIGINATED_CALL_ID)) { 412 BluetoothHeadsetClientCall c = mCalls.get(HF_ORIGINATED_CALL_ID); 413 long cCreationElapsed = c.getCreationElapsedMilli(); 414 if (callAddedIds.size() > 0) { 415 if (DBG) { 416 Log.d(TAG, "Associating the first call with HF originated call"); 417 } 418 hfOriginatedAssoc = (Integer) callAddedIds.toArray()[0]; 419 mCalls.put(hfOriginatedAssoc, mCalls.get(HF_ORIGINATED_CALL_ID)); 420 mCalls.remove(HF_ORIGINATED_CALL_ID); 421 422 // Adjust this call in above sets. 423 callAddedIds.remove(hfOriginatedAssoc); 424 callRetainedIds.add(hfOriginatedAssoc); 425 } else if (SystemClock.elapsedRealtime() - cCreationElapsed > OUTGOING_TIMEOUT_MILLI) { 426 Log.w(TAG, "Outgoing call did not see a response, clear the calls and send CHUP"); 427 terminateCall(); 428 429 // Clean out the state. 430 for (Integer idx : mCalls.keySet()) { 431 BluetoothHeadsetClientCall c1 = mCalls.get(idx); 432 c1.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED); 433 sendCallChangedIntent(c); 434 } 435 mCalls.clear(); 436 } 437 } 438 439 if (DBG) { 440 Log.d(TAG, "ADJUST: currCallIdSet " + mCalls.keySet() + " newCallIdSet " + 441 newCallIdSet + " callAddedIds " + callAddedIds + " callRemovedIds " + 442 callRemovedIds + " callRetainedIds " + callRetainedIds); 443 } 444 445 // Terminate & remove the calls that are done. 446 for (Integer idx : callRemovedIds) { 447 BluetoothHeadsetClientCall c = mCalls.remove(idx); 448 c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED); 449 sendCallChangedIntent(c); 450 } 451 452 // Add the new calls. 453 for (Integer idx : callAddedIds) { 454 BluetoothHeadsetClientCall c = mCallsUpdate.get(idx); 455 mCalls.put(idx, c); 456 sendCallChangedIntent(c); 457 } 458 459 // Update the existing calls. 460 for (Integer idx : callRetainedIds) { 461 BluetoothHeadsetClientCall cOrig = mCalls.get(idx); 462 BluetoothHeadsetClientCall cUpdate = mCallsUpdate.get(idx); 463 464 // Update the necessary fields. 465 cOrig.setNumber(cUpdate.getNumber()); 466 cOrig.setState(cUpdate.getState()); 467 cOrig.setMultiParty(cUpdate.isMultiParty()); 468 469 // Send update with original object (UUID, idx). 470 sendCallChangedIntent(cOrig); 471 } 472 473 if (loopQueryCalls()) { 474 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS); 475 } 476 477 mCallsUpdate.clear(); 478 } 479 480 private void queryCallsUpdate(int id, int state, String number, boolean multiParty, 481 boolean outgoing) { 482 if (DBG) { 483 Log.d(TAG, "queryCallsUpdate: " + id); 484 } 485 mCallsUpdate.put(id, new BluetoothHeadsetClientCall(mCurrentDevice, id, state, number, 486 multiParty, outgoing)); 487 } 488 489 // helper function for determining if query calls should be looped 490 private boolean loopQueryCalls() { 491 if (DBG) { 492 Log.d(TAG, "loopQueryCalls, starting call query loop"); 493 } 494 if (mCalls.size() > 0) { 495 return true; 496 } 497 498 // Workaround for Windows Phone 7.8 not sending callsetup=0 after 499 // rejecting incoming call in 3WC use case (when no active calls present). 500 // Fixes both, AG and HF rejecting the call. 501 BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING); 502 if (c != null && mIndicatorCallSetup == HeadsetClientHalConstants.CALLSETUP_NONE) 503 return true; 504 505 return false; 506 } 507 508 private void acceptCall(int flag, boolean retry) { 509 int action = -1; 510 511 if (DBG) { 512 Log.d(TAG, "acceptCall: (" + flag + ")"); 513 } 514 515 BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING, 516 BluetoothHeadsetClientCall.CALL_STATE_WAITING); 517 if (c == null) { 518 c = getCall(BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD, 519 BluetoothHeadsetClientCall.CALL_STATE_HELD); 520 521 if (c == null) { 522 return; 523 } 524 } 525 526 if (DBG) { 527 Log.d(TAG, "Call to accept: " + c); 528 } 529 switch (c.getState()) { 530 case BluetoothHeadsetClientCall.CALL_STATE_INCOMING: 531 if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) { 532 return; 533 } 534 535 // Some NOKIA phones with Windows Phone 7.8 and MeeGo requires CHLD=1 536 // for accepting incoming call if it is the only call present after 537 // second active remote has disconnected (3WC scenario - call state 538 // changes from waiting to incoming). On the other hand some Android 539 // phones and iPhone requires ATA. Try to handle those gently by 540 // first issuing ATA. Failing means that AG is probably one of those 541 // phones that requires CHLD=1. Handle this case when we are retrying. 542 // Accepting incoming calls when there is held one and 543 // no active should also be handled by ATA. 544 action = HeadsetClientHalConstants.CALL_ACTION_ATA; 545 546 if (mCalls.size() == 1 && retry) { 547 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1; 548 } 549 break; 550 case BluetoothHeadsetClientCall.CALL_STATE_WAITING: 551 if (callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) == 0) { 552 // if no active calls present only plain accept is allowed 553 if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) { 554 return; 555 } 556 557 // Some phones (WP7) require ATA instead of CHLD=2 558 // to accept waiting call if no active calls are present. 559 if (retry) { 560 action = HeadsetClientHalConstants.CALL_ACTION_ATA; 561 } else { 562 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 563 } 564 break; 565 } 566 567 // if active calls are present then we have the option to either terminate the 568 // existing call or hold the existing call. We hold the other call by default. 569 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD || 570 flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) { 571 if (DBG) { 572 Log.d(TAG, "Accepting call with accept and hold"); 573 } 574 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 575 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) { 576 if (DBG) { 577 Log.d(TAG, "Accepting call with accept and reject"); 578 } 579 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1; 580 } else { 581 Log.e(TAG, "Aceept call with invalid flag: " + flag); 582 return; 583 } 584 break; 585 case BluetoothHeadsetClientCall.CALL_STATE_HELD: 586 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) { 587 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 588 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) { 589 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1; 590 } else if (getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) != null) { 591 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3; 592 } else { 593 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 594 } 595 break; 596 case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: 597 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_1; 598 break; 599 case BluetoothHeadsetClientCall.CALL_STATE_ALERTING: 600 case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE: 601 case BluetoothHeadsetClientCall.CALL_STATE_DIALING: 602 default: 603 return; 604 } 605 606 if (handleCallActionNative(action, 0)) { 607 addQueuedAction(ACCEPT_CALL, action); 608 } else { 609 Log.e(TAG, "ERROR: Couldn't accept a call, action:" + action); 610 } 611 } 612 613 private void rejectCall() { 614 int action; 615 616 if (DBG) { 617 Log.d(TAG, "rejectCall"); 618 } 619 620 BluetoothHeadsetClientCall c = 621 getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING, 622 BluetoothHeadsetClientCall.CALL_STATE_WAITING, 623 BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD, 624 BluetoothHeadsetClientCall.CALL_STATE_HELD); 625 if (c == null) { 626 if (DBG) { 627 Log.d(TAG, "No call to reject, returning."); 628 } 629 return; 630 } 631 632 switch (c.getState()) { 633 case BluetoothHeadsetClientCall.CALL_STATE_INCOMING: 634 action = HeadsetClientHalConstants.CALL_ACTION_CHUP; 635 break; 636 case BluetoothHeadsetClientCall.CALL_STATE_WAITING: 637 case BluetoothHeadsetClientCall.CALL_STATE_HELD: 638 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0; 639 break; 640 case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: 641 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_2; 642 break; 643 case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE: 644 case BluetoothHeadsetClientCall.CALL_STATE_DIALING: 645 case BluetoothHeadsetClientCall.CALL_STATE_ALERTING: 646 default: 647 return; 648 } 649 650 if (DBG) { 651 Log.d(TAG, "Reject call action " + action); 652 } 653 if (handleCallActionNative(action, 0)) { 654 addQueuedAction(REJECT_CALL, action); 655 } else { 656 Log.e(TAG, "ERROR: Couldn't reject a call, action:" + action); 657 } 658 } 659 660 private void holdCall() { 661 int action; 662 663 if (DBG) { 664 Log.d(TAG, "holdCall"); 665 } 666 667 BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING); 668 if (c != null) { 669 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_0; 670 } else { 671 c = getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE); 672 if (c == null) { 673 return; 674 } 675 676 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 677 } 678 679 if (handleCallActionNative(action, 0)) { 680 addQueuedAction(HOLD_CALL, action); 681 } else { 682 Log.e(TAG, "ERROR: Couldn't hold a call, action:" + action); 683 } 684 } 685 686 private void terminateCall() { 687 if (DBG) { 688 Log.d(TAG, "terminateCall"); 689 } 690 691 int action = HeadsetClientHalConstants.CALL_ACTION_CHUP; 692 693 BluetoothHeadsetClientCall c = getCall( 694 BluetoothHeadsetClientCall.CALL_STATE_DIALING, 695 BluetoothHeadsetClientCall.CALL_STATE_ALERTING, 696 BluetoothHeadsetClientCall.CALL_STATE_ACTIVE); 697 if (c != null) { 698 if (handleCallActionNative(action, 0)) { 699 addQueuedAction(TERMINATE_CALL, action); 700 } else { 701 Log.e(TAG, "ERROR: Couldn't terminate outgoing call"); 702 } 703 } 704 } 705 706 private void enterPrivateMode(int idx) { 707 if (DBG) { 708 Log.d(TAG, "enterPrivateMode: " + idx); 709 } 710 711 BluetoothHeadsetClientCall c = mCalls.get(idx); 712 713 if (c == null) { 714 return; 715 } 716 717 if (c.getState() != BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) { 718 return; 719 } 720 721 if (!c.isMultiParty()) { 722 return; 723 } 724 725 if (handleCallActionNative(HeadsetClientHalConstants.CALL_ACTION_CHLD_2x, idx)) { 726 addQueuedAction(ENTER_PRIVATE_MODE, c); 727 } else { 728 Log.e(TAG, "ERROR: Couldn't enter private " + " id:" + idx); 729 } 730 } 731 732 private void explicitCallTransfer() { 733 if (DBG) { 734 Log.d(TAG, "explicitCallTransfer"); 735 } 736 737 // can't transfer call if there is not enough call parties 738 if (mCalls.size() < 2) { 739 return; 740 } 741 742 if (handleCallActionNative(HeadsetClientHalConstants.CALL_ACTION_CHLD_4, -1)) { 743 addQueuedAction(EXPLICIT_CALL_TRANSFER); 744 } else { 745 Log.e(TAG, "ERROR: Couldn't transfer call"); 746 } 747 } 748 749 public Bundle getCurrentAgFeatures() 750 { 751 Bundle b = new Bundle(); 752 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY) == 753 HeadsetClientHalConstants.PEER_FEAT_3WAY) { 754 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true); 755 } 756 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC) == 757 HeadsetClientHalConstants.PEER_FEAT_VREC) { 758 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true); 759 } 760 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VTAG) == 761 HeadsetClientHalConstants.PEER_FEAT_VTAG) { 762 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT, true); 763 } 764 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT) == 765 HeadsetClientHalConstants.PEER_FEAT_REJECT) { 766 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true); 767 } 768 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC) == 769 HeadsetClientHalConstants.PEER_FEAT_ECC) { 770 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true); 771 } 772 773 // add individual CHLD support extras 774 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) == 775 HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) { 776 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true); 777 } 778 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL) == 779 HeadsetClientHalConstants.CHLD_FEAT_REL) { 780 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true); 781 } 782 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) == 783 HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) { 784 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true); 785 } 786 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE) == 787 HeadsetClientHalConstants.CHLD_FEAT_MERGE) { 788 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true); 789 } 790 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) == 791 HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) { 792 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true); 793 } 794 795 return b; 796 } 797 798 private HeadsetClientStateMachine(HeadsetClientService context) { 799 super(TAG); 800 mService = context; 801 802 mAdapter = BluetoothAdapter.getDefaultAdapter(); 803 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 804 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 805 mAudioWbs = false; 806 807 mAudioRouteAllowed = context.getResources().getBoolean( 808 R.bool.headset_client_initial_audio_route_allowed); 809 810 mTelecomManager = (TelecomManager) context.getSystemService(context.TELECOM_SERVICE); 811 812 mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE; 813 mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME; 814 mIndicatorNetworkSignal = 0; 815 mIndicatorBatteryLevel = 0; 816 817 // all will be set on connected 818 mIndicatorCall = -1; 819 mIndicatorCallSetup = -1; 820 mIndicatorCallHeld = -1; 821 822 mMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); 823 mMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); 824 825 mOperatorName = null; 826 mSubscriberInfo = null; 827 828 mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED; 829 mInBandRingtone = HeadsetClientHalConstants.IN_BAND_RING_NOT_PROVIDED; 830 831 mQueuedActions = new LinkedList<Pair<Integer, Object>>(); 832 clearPendingAction(); 833 834 mCalls.clear(); 835 mCallsUpdate.clear(); 836 837 initializeNative(); 838 mNativeAvailable = true; 839 840 mDisconnected = new Disconnected(); 841 mConnecting = new Connecting(); 842 mConnected = new Connected(); 843 mAudioOn = new AudioOn(); 844 845 addState(mDisconnected); 846 addState(mConnecting); 847 addState(mConnected); 848 addState(mAudioOn, mConnected); 849 850 setInitialState(mDisconnected); 851 } 852 853 static HeadsetClientStateMachine make(HeadsetClientService context) { 854 if (DBG) { 855 Log.d(TAG, "make"); 856 } 857 HeadsetClientStateMachine hfcsm = new HeadsetClientStateMachine(context); 858 hfcsm.start(); 859 return hfcsm; 860 } 861 862 public void doQuit() { 863 Log.d(TAG, "doQuit"); 864 quitNow(); 865 } 866 867 public void cleanup() { 868 if (mNativeAvailable) { 869 cleanupNative(); 870 mNativeAvailable = false; 871 } 872 } 873 874 private int hfToAmVol(int hfVol) { 875 int amRange = mMaxAmVcVol - mMinAmVcVol; 876 int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME; 877 int amOffset = 878 (amRange * (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)) / hfRange; 879 int amVol = mMinAmVcVol + amOffset; 880 Log.d(TAG, "HF -> AM " + hfVol + " " + amVol); 881 return amVol; 882 } 883 884 private int amToHfVol(int amVol) { 885 int amRange = mMaxAmVcVol - mMinAmVcVol; 886 int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME; 887 int hfOffset = (hfRange * (amVol - mMinAmVcVol)) / amRange; 888 int hfVol = MIN_HFP_SCO_VOICE_CALL_VOLUME + hfOffset; 889 Log.d(TAG, "AM -> HF " + amVol + " " + hfVol); 890 return hfVol; 891 } 892 893 private class Disconnected extends State { 894 @Override 895 public void enter() { 896 Log.d(TAG, "Enter Disconnected: " + getCurrentMessage().what); 897 898 // cleanup 899 mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE; 900 mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME; 901 mIndicatorNetworkSignal = 0; 902 mIndicatorBatteryLevel = 0; 903 904 mAudioWbs = false; 905 906 // will be set on connect 907 mIndicatorCall = -1; 908 mIndicatorCallSetup = -1; 909 mIndicatorCallHeld = -1; 910 911 mOperatorName = null; 912 mSubscriberInfo = null; 913 914 mQueuedActions = new LinkedList<Pair<Integer, Object>>(); 915 clearPendingAction(); 916 917 mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED; 918 mInBandRingtone = HeadsetClientHalConstants.IN_BAND_RING_NOT_PROVIDED; 919 920 mCalls.clear(); 921 mCallsUpdate.clear(); 922 923 mPeerFeatures = 0; 924 mChldFeatures = 0; 925 926 removeMessages(QUERY_CURRENT_CALLS); 927 } 928 929 @Override 930 public synchronized boolean processMessage(Message message) { 931 Log.d(TAG, "Disconnected process message: " + message.what); 932 933 if (mCurrentDevice != null) { 934 Log.e(TAG, "ERROR: current device not null in Disconnected"); 935 return NOT_HANDLED; 936 } 937 938 switch (message.what) { 939 case CONNECT: 940 BluetoothDevice device = (BluetoothDevice) message.obj; 941 942 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 943 BluetoothProfile.STATE_DISCONNECTED); 944 945 if (!connectNative(getByteAddress(device))) { 946 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 947 BluetoothProfile.STATE_CONNECTING); 948 break; 949 } 950 951 mCurrentDevice = device; 952 953 transitionTo(mConnecting); 954 break; 955 case DISCONNECT: 956 // ignore 957 break; 958 case STACK_EVENT: 959 StackEvent event = (StackEvent) message.obj; 960 if (DBG) { 961 Log.d(TAG, "Stack event type: " + event.type); 962 } 963 switch (event.type) { 964 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 965 if (DBG) { 966 Log.d(TAG, "Disconnected: Connection " + event.device 967 + " state changed:" + event.valueInt); 968 } 969 processConnectionEvent(event.valueInt, event.device); 970 break; 971 default: 972 Log.e(TAG, "Disconnected: Unexpected stack event: " + event.type); 973 break; 974 } 975 break; 976 default: 977 return NOT_HANDLED; 978 } 979 return HANDLED; 980 } 981 982 // in Disconnected state 983 private void processConnectionEvent(int state, BluetoothDevice device) 984 { 985 switch (state) { 986 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED: 987 Log.w(TAG, "HFPClient Connecting from Disconnected state"); 988 if (okToConnect(device)) { 989 Log.i(TAG, "Incoming AG accepted"); 990 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 991 BluetoothProfile.STATE_DISCONNECTED); 992 mCurrentDevice = device; 993 transitionTo(mConnecting); 994 } else { 995 Log.i(TAG, "Incoming AG rejected. priority=" + mService.getPriority(device) 996 + 997 " bondState=" + device.getBondState()); 998 // reject the connection and stay in Disconnected state 999 // itself 1000 disconnectNative(getByteAddress(device)); 1001 // the other profile connection should be initiated 1002 AdapterService adapterService = AdapterService.getAdapterService(); 1003 if (adapterService != null) { 1004 adapterService.connectOtherProfile(device, 1005 AdapterService.PROFILE_CONN_REJECTED); 1006 } 1007 } 1008 break; 1009 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING: 1010 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1011 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING: 1012 default: 1013 Log.i(TAG, "ignoring state: " + state); 1014 break; 1015 } 1016 } 1017 1018 @Override 1019 public void exit() { 1020 if (DBG) { 1021 Log.d(TAG, "Exit Disconnected: " + getCurrentMessage().what); 1022 } 1023 } 1024 } 1025 1026 private class Connecting extends State { 1027 @Override 1028 public void enter() { 1029 if (DBG) { 1030 Log.d(TAG, "Enter Connecting: " + getCurrentMessage().what); 1031 } 1032 // This message is either consumed in processMessage or 1033 // removed in exit. It is safe to send a CONNECTING_TIMEOUT here since 1034 // the only transition is when connection attempt is initiated. 1035 sendMessageDelayed(CONNECTING_TIMEOUT, CONNECTING_TIMEOUT_MS); 1036 } 1037 1038 @Override 1039 public synchronized boolean processMessage(Message message) { 1040 if (DBG) { 1041 Log.d(TAG, "Connecting process message: " + message.what); 1042 } 1043 1044 switch (message.what) { 1045 case CONNECT: 1046 case CONNECT_AUDIO: 1047 case DISCONNECT: 1048 deferMessage(message); 1049 break; 1050 case STACK_EVENT: 1051 StackEvent event = (StackEvent) message.obj; 1052 if (DBG) { 1053 Log.d(TAG, "Connecting: event type: " + event.type); 1054 } 1055 switch (event.type) { 1056 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1057 if (DBG) { 1058 Log.d(TAG, "Connecting: Connection " + event.device + " state changed:" 1059 + event.valueInt); 1060 } 1061 processConnectionEvent(event.valueInt, event.valueInt2, 1062 event.valueInt3, event.device); 1063 break; 1064 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1065 case EVENT_TYPE_VR_STATE_CHANGED: 1066 case EVENT_TYPE_NETWORK_STATE: 1067 case EVENT_TYPE_ROAMING_STATE: 1068 case EVENT_TYPE_NETWORK_SIGNAL: 1069 case EVENT_TYPE_BATTERY_LEVEL: 1070 case EVENT_TYPE_CALL: 1071 case EVENT_TYPE_CALLSETUP: 1072 case EVENT_TYPE_CALLHELD: 1073 case EVENT_TYPE_RESP_AND_HOLD: 1074 case EVENT_TYPE_CLIP: 1075 case EVENT_TYPE_CALL_WAITING: 1076 case EVENT_TYPE_VOLUME_CHANGED: 1077 case EVENT_TYPE_IN_BAND_RING: 1078 deferMessage(message); 1079 break; 1080 case EVENT_TYPE_CMD_RESULT: 1081 case EVENT_TYPE_SUBSCRIBER_INFO: 1082 case EVENT_TYPE_CURRENT_CALLS: 1083 case EVENT_TYPE_OPERATOR_NAME: 1084 default: 1085 Log.e(TAG, "Connecting: ignoring stack event: " + event.type); 1086 break; 1087 } 1088 break; 1089 case CONNECTING_TIMEOUT: 1090 // We timed out trying to connect, transition to disconnected. 1091 Log.w(TAG, "Connection timeout for " + mCurrentDevice); 1092 transitionTo(mDisconnected); 1093 broadcastConnectionState( 1094 mCurrentDevice, 1095 BluetoothProfile.STATE_CONNECTING, 1096 BluetoothProfile.STATE_DISCONNECTED); 1097 break; 1098 1099 default: 1100 Log.w(TAG, "Message not handled " + message); 1101 return NOT_HANDLED; 1102 } 1103 return HANDLED; 1104 } 1105 1106 // in Connecting state 1107 private void processConnectionEvent( 1108 int state, int peer_feat, int chld_feat, BluetoothDevice device) { 1109 switch (state) { 1110 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1111 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 1112 BluetoothProfile.STATE_CONNECTING); 1113 mCurrentDevice = null; 1114 transitionTo(mDisconnected); 1115 break; 1116 1117 case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED: 1118 Log.d(TAG, "HFPClient Connected from Connecting state"); 1119 1120 mPeerFeatures = peer_feat; 1121 mChldFeatures = chld_feat; 1122 1123 // We do not support devices which do not support enhanced call status (ECS). 1124 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECS) == 0) { 1125 disconnectNative(getByteAddress(device)); 1126 return; 1127 } 1128 1129 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 1130 BluetoothProfile.STATE_CONNECTING); 1131 1132 // Send AT+NREC to remote if supported by audio 1133 if (HeadsetClientHalConstants.HANDSFREECLIENT_NREC_SUPPORTED && 1134 ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECNR) == 1135 HeadsetClientHalConstants.PEER_FEAT_ECNR)) { 1136 if (sendATCmdNative(HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_NREC, 1137 1 , 0, null)) { 1138 addQueuedAction(DISABLE_NREC); 1139 } else { 1140 Log.e(TAG, "Failed to send NREC"); 1141 } 1142 } 1143 transitionTo(mConnected); 1144 1145 int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1146 sendMessage( 1147 obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0)); 1148 // Mic is either in ON state (full volume) or OFF state. There is no way in 1149 // Android to change the MIC volume. 1150 sendMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME, 1151 mAudioManager.isMicrophoneMute() ? 0 : 15, 0)); 1152 1153 // query subscriber info 1154 sendMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO); 1155 break; 1156 1157 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED: 1158 if (!mCurrentDevice.equals(device)) { 1159 Log.w(TAG, "incoming connection event, device: " + device); 1160 1161 broadcastConnectionState(mCurrentDevice, 1162 BluetoothProfile.STATE_DISCONNECTED, 1163 BluetoothProfile.STATE_CONNECTING); 1164 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 1165 BluetoothProfile.STATE_DISCONNECTED); 1166 1167 mCurrentDevice = device; 1168 } 1169 break; 1170 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING: 1171 /* outgoing connecting started */ 1172 if (DBG) { 1173 Log.d(TAG, "outgoing connection started, ignore"); 1174 } 1175 break; 1176 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING: 1177 default: 1178 Log.e(TAG, "Incorrect state: " + state); 1179 break; 1180 } 1181 } 1182 1183 @Override 1184 public void exit() { 1185 if (DBG) { 1186 Log.d(TAG, "Exit Connecting: " + getCurrentMessage().what); 1187 } 1188 removeMessages(CONNECTING_TIMEOUT); 1189 } 1190 } 1191 1192 private class Connected extends State { 1193 @Override 1194 public void enter() { 1195 if (DBG) { 1196 Log.d(TAG, "Enter Connected: " + getCurrentMessage().what); 1197 } 1198 mAudioWbs = false; 1199 } 1200 1201 @Override 1202 public synchronized boolean processMessage(Message message) { 1203 if (DBG) { 1204 Log.d(TAG, "Connected process message: " + message.what); 1205 } 1206 if (DBG) { 1207 if (mCurrentDevice == null) { 1208 Log.e(TAG, "ERROR: mCurrentDevice is null in Connected"); 1209 return NOT_HANDLED; 1210 } 1211 } 1212 1213 switch (message.what) { 1214 case CONNECT: 1215 BluetoothDevice device = (BluetoothDevice) message.obj; 1216 if (mCurrentDevice.equals(device)) { 1217 // already connected to this device, do nothing 1218 break; 1219 } 1220 1221 if (!disconnectNative(getByteAddress(mCurrentDevice))) { 1222 // if succeed this will be handled from disconnected 1223 // state 1224 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 1225 BluetoothProfile.STATE_DISCONNECTED); 1226 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1227 BluetoothProfile.STATE_CONNECTING); 1228 break; 1229 } 1230 1231 // will be handled when entered disconnected 1232 deferMessage(message); 1233 break; 1234 case DISCONNECT: 1235 BluetoothDevice dev = (BluetoothDevice) message.obj; 1236 if (!mCurrentDevice.equals(dev)) { 1237 break; 1238 } 1239 broadcastConnectionState(dev, BluetoothProfile.STATE_DISCONNECTING, 1240 BluetoothProfile.STATE_CONNECTED); 1241 if (!disconnectNative(getByteAddress(dev))) { 1242 // disconnecting failed 1243 broadcastConnectionState(dev, BluetoothProfile.STATE_CONNECTED, 1244 BluetoothProfile.STATE_DISCONNECTED); 1245 break; 1246 } 1247 break; 1248 case CONNECT_AUDIO: 1249 // TODO: handle audio connection failure 1250 if (!connectAudioNative(getByteAddress(mCurrentDevice))) { 1251 Log.e(TAG, "ERROR: Couldn't connect Audio."); 1252 } 1253 break; 1254 case DISCONNECT_AUDIO: 1255 // TODO: handle audio disconnection failure 1256 if (!disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1257 Log.e(TAG, "ERROR: Couldn't connect Audio."); 1258 } 1259 break; 1260 case VOICE_RECOGNITION_START: 1261 if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STOPPED) { 1262 if (startVoiceRecognitionNative()) { 1263 addQueuedAction(VOICE_RECOGNITION_START); 1264 } else { 1265 Log.e(TAG, "ERROR: Couldn't start voice recognition"); 1266 } 1267 } 1268 break; 1269 case VOICE_RECOGNITION_STOP: 1270 if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STARTED) { 1271 if (stopVoiceRecognitionNative()) { 1272 addQueuedAction(VOICE_RECOGNITION_STOP); 1273 } else { 1274 Log.e(TAG, "ERROR: Couldn't stop voice recognition"); 1275 } 1276 } 1277 break; 1278 // Called only for Mute/Un-mute - Mic volume change is not allowed. 1279 case SET_MIC_VOLUME: 1280 if (mVgmFromStack) { 1281 mVgmFromStack = false; 1282 break; 1283 } 1284 if (setVolumeNative(HeadsetClientHalConstants.VOLUME_TYPE_MIC, message.arg1)) { 1285 addQueuedAction(SET_MIC_VOLUME); 1286 } 1287 break; 1288 case SET_SPEAKER_VOLUME: 1289 // This message should always contain the volume in AudioManager max normalized. 1290 int amVol = message.arg1; 1291 int hfVol = amToHfVol(amVol); 1292 Log.d(TAG,"HF volume is set to " + hfVol); 1293 mAudioManager.setParameters("hfp_volume=" + hfVol); 1294 if (mVgsFromStack) { 1295 mVgsFromStack = false; 1296 break; 1297 } 1298 if (setVolumeNative(HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) { 1299 addQueuedAction(SET_SPEAKER_VOLUME); 1300 } 1301 break; 1302 case DIAL_NUMBER: 1303 // Add the call as an outgoing call. 1304 BluetoothHeadsetClientCall c = (BluetoothHeadsetClientCall) message.obj; 1305 mCalls.put(HF_ORIGINATED_CALL_ID, c); 1306 1307 if (dialNative(c.getNumber())) { 1308 addQueuedAction(DIAL_NUMBER, c.getNumber()); 1309 // Start looping on calling current calls. 1310 sendMessage(QUERY_CURRENT_CALLS); 1311 } else { 1312 Log.e(TAG, "ERROR: Cannot dial with a given number:" + (String) message.obj); 1313 // Set the call to terminated remove. 1314 c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED); 1315 sendCallChangedIntent(c); 1316 mCalls.remove(HF_ORIGINATED_CALL_ID); 1317 } 1318 break; 1319 case ACCEPT_CALL: 1320 acceptCall(message.arg1, false); 1321 break; 1322 case REJECT_CALL: 1323 rejectCall(); 1324 break; 1325 case HOLD_CALL: 1326 holdCall(); 1327 break; 1328 case TERMINATE_CALL: 1329 terminateCall(); 1330 break; 1331 case ENTER_PRIVATE_MODE: 1332 enterPrivateMode(message.arg1); 1333 break; 1334 case EXPLICIT_CALL_TRANSFER: 1335 explicitCallTransfer(); 1336 break; 1337 case SEND_DTMF: 1338 if (sendDtmfNative((byte) message.arg1)) { 1339 addQueuedAction(SEND_DTMF); 1340 } else { 1341 Log.e(TAG, "ERROR: Couldn't send DTMF"); 1342 } 1343 break; 1344 case SUBSCRIBER_INFO: 1345 if (retrieveSubscriberInfoNative()) { 1346 addQueuedAction(SUBSCRIBER_INFO); 1347 } else { 1348 Log.e(TAG, "ERROR: Couldn't retrieve subscriber info"); 1349 } 1350 break; 1351 case LAST_VTAG_NUMBER: 1352 if (requestLastVoiceTagNumberNative()) { 1353 addQueuedAction(LAST_VTAG_NUMBER); 1354 } else { 1355 Log.e(TAG, "ERROR: Couldn't get last VTAG number"); 1356 } 1357 break; 1358 case QUERY_CURRENT_CALLS: 1359 // Whenever the timer expires we query calls if there are outstanding requests 1360 // for query calls. 1361 long currentElapsed = SystemClock.elapsedRealtime(); 1362 if (mClccTimer < currentElapsed) { 1363 queryCallsStart(); 1364 mClccTimer = currentElapsed + QUERY_CURRENT_CALLS_WAIT_MILLIS; 1365 // Request satisfied, ignore all other call query messages. 1366 removeMessages(QUERY_CURRENT_CALLS); 1367 } else { 1368 // Replace all messages with one concrete message. 1369 removeMessages(QUERY_CURRENT_CALLS); 1370 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS); 1371 } 1372 break; 1373 case STACK_EVENT: 1374 Intent intent = null; 1375 StackEvent event = (StackEvent) message.obj; 1376 if (DBG) { 1377 Log.d(TAG, "Connected: event type: " + event.type); 1378 } 1379 1380 switch (event.type) { 1381 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1382 if (DBG) { 1383 Log.d(TAG, "Connected: Connection state changed: " + event.device 1384 + ": " + event.valueInt); 1385 } 1386 processConnectionEvent(event.valueInt, event.device); 1387 break; 1388 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1389 if (DBG) { 1390 Log.d(TAG, "Connected: Audio state changed: " + event.device + ": " 1391 + event.valueInt); 1392 } 1393 processAudioEvent(event.valueInt, event.device); 1394 break; 1395 case EVENT_TYPE_NETWORK_STATE: 1396 if (DBG) { 1397 Log.d(TAG, "Connected: Network state: " + event.valueInt); 1398 } 1399 mIndicatorNetworkState = event.valueInt; 1400 1401 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1402 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, 1403 event.valueInt); 1404 1405 if (mIndicatorNetworkState == 1406 HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE) { 1407 mOperatorName = null; 1408 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, 1409 mOperatorName); 1410 } 1411 1412 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1413 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1414 1415 if (mIndicatorNetworkState == 1416 HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE) { 1417 if (queryCurrentOperatorNameNative()) { 1418 addQueuedAction(QUERY_OPERATOR_NAME); 1419 } else { 1420 Log.e(TAG, "ERROR: Couldn't querry operator name"); 1421 } 1422 } 1423 break; 1424 case EVENT_TYPE_ROAMING_STATE: 1425 mIndicatorNetworkType = event.valueInt; 1426 1427 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1428 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, 1429 event.valueInt); 1430 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1431 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1432 break; 1433 case EVENT_TYPE_NETWORK_SIGNAL: 1434 mIndicatorNetworkSignal = event.valueInt; 1435 1436 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1437 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, 1438 event.valueInt); 1439 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1440 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1441 break; 1442 case EVENT_TYPE_BATTERY_LEVEL: 1443 mIndicatorBatteryLevel = event.valueInt; 1444 1445 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1446 intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, 1447 event.valueInt); 1448 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1449 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1450 break; 1451 case EVENT_TYPE_OPERATOR_NAME: 1452 mOperatorName = event.valueString; 1453 1454 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1455 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, 1456 event.valueString); 1457 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1458 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1459 break; 1460 case EVENT_TYPE_VR_STATE_CHANGED: 1461 if (mVoiceRecognitionActive != event.valueInt) { 1462 mVoiceRecognitionActive = event.valueInt; 1463 1464 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1465 intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, 1466 mVoiceRecognitionActive); 1467 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1468 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1469 } 1470 break; 1471 case EVENT_TYPE_CALL: 1472 updateCallIndicator(event.valueInt); 1473 break; 1474 case EVENT_TYPE_CALLSETUP: 1475 updateCallSetupIndicator(event.valueInt); 1476 break; 1477 case EVENT_TYPE_CALLHELD: 1478 updateCallHeldIndicator(event.valueInt); 1479 break; 1480 case EVENT_TYPE_RESP_AND_HOLD: 1481 updateRespAndHold(event.valueInt); 1482 break; 1483 case EVENT_TYPE_CLIP: 1484 updateClip(event.valueString); 1485 break; 1486 case EVENT_TYPE_CALL_WAITING: 1487 updateCCWA(event.valueString); 1488 break; 1489 case EVENT_TYPE_IN_BAND_RING: 1490 if (mInBandRingtone != event.valueInt) { 1491 mInBandRingtone = event.valueInt; 1492 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1493 intent.putExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, 1494 mInBandRingtone); 1495 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1496 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1497 } 1498 break; 1499 case EVENT_TYPE_CURRENT_CALLS: 1500 queryCallsUpdate( 1501 event.valueInt, 1502 event.valueInt3, 1503 event.valueString, 1504 event.valueInt4 == 1505 HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI, 1506 event.valueInt2 == 1507 HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING); 1508 break; 1509 case EVENT_TYPE_VOLUME_CHANGED: 1510 if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) { 1511 Log.d(TAG, "AM volume set to " + 1512 hfToAmVol(event.valueInt2)); 1513 mAudioManager.setStreamVolume( 1514 AudioManager.STREAM_VOICE_CALL, 1515 hfToAmVol(event.valueInt2), 1516 AudioManager.FLAG_SHOW_UI); 1517 mVgsFromStack = true; 1518 } else if (event.valueInt == 1519 HeadsetClientHalConstants.VOLUME_TYPE_MIC) { 1520 mAudioManager.setMicrophoneMute(event.valueInt2 == 0); 1521 1522 mVgmFromStack = true; 1523 } 1524 break; 1525 case EVENT_TYPE_CMD_RESULT: 1526 Pair<Integer, Object> queuedAction = mQueuedActions.poll(); 1527 1528 // should not happen but... 1529 if (queuedAction == null || queuedAction.first == NO_ACTION) { 1530 clearPendingAction(); 1531 break; 1532 } 1533 1534 if (DBG) { 1535 Log.d(TAG, "Connected: command result: " + event.valueInt 1536 + " queuedAction: " + queuedAction.first); 1537 } 1538 1539 switch (queuedAction.first) { 1540 case VOICE_RECOGNITION_STOP: 1541 case VOICE_RECOGNITION_START: 1542 if (event.valueInt == HeadsetClientHalConstants.CMD_COMPLETE_OK) { 1543 if (queuedAction.first == VOICE_RECOGNITION_STOP) { 1544 mVoiceRecognitionActive = 1545 HeadsetClientHalConstants.VR_STATE_STOPPED; 1546 } else { 1547 mVoiceRecognitionActive = 1548 HeadsetClientHalConstants.VR_STATE_STARTED; 1549 } 1550 } 1551 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1552 intent.putExtra( 1553 BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, 1554 mVoiceRecognitionActive); 1555 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1556 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1557 break; 1558 case QUERY_CURRENT_CALLS: 1559 queryCallsDone(); 1560 break; 1561 case ACCEPT_CALL: 1562 if (event.valueInt == BluetoothHeadsetClient.ACTION_RESULT_OK) { 1563 mPendingAction = queuedAction; 1564 } else { 1565 if (callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) == 0) { 1566 if(getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING) != null && 1567 (Integer) mPendingAction.second == HeadsetClientHalConstants.CALL_ACTION_ATA) { 1568 acceptCall(BluetoothHeadsetClient.CALL_ACCEPT_NONE, true); 1569 break; 1570 } else if(getCall(BluetoothHeadsetClientCall.CALL_STATE_WAITING) != null && 1571 (Integer) mPendingAction.second == HeadsetClientHalConstants.CALL_ACTION_CHLD_2) { 1572 acceptCall(BluetoothHeadsetClient.CALL_ACCEPT_NONE, true); 1573 break; 1574 } 1575 } 1576 sendActionResultIntent(event); 1577 } 1578 break; 1579 case DIAL_NUMBER: 1580 case REJECT_CALL: 1581 case HOLD_CALL: 1582 case TERMINATE_CALL: 1583 case ENTER_PRIVATE_MODE: 1584 case TERMINATE_SPECIFIC_CALL: 1585 // if terminating specific succeed no other 1586 // event is send 1587 if (event.valueInt != BluetoothHeadsetClient.ACTION_RESULT_OK) { 1588 sendActionResultIntent(event); 1589 } 1590 break; 1591 case LAST_VTAG_NUMBER: 1592 if (event.valueInt != BluetoothHeadsetClient.ACTION_RESULT_OK) { 1593 sendActionResultIntent(event); 1594 } 1595 break; 1596 case DISABLE_NREC: 1597 if (event.valueInt != HeadsetClientHalConstants.CMD_COMPLETE_OK) { 1598 Log.w(TAG, "Failed to disable AG's EC and NR"); 1599 } 1600 break; 1601 case SET_MIC_VOLUME: 1602 case SET_SPEAKER_VOLUME: 1603 case SUBSCRIBER_INFO: 1604 case QUERY_OPERATOR_NAME: 1605 break; 1606 default: 1607 sendActionResultIntent(event); 1608 break; 1609 } 1610 1611 break; 1612 case EVENT_TYPE_SUBSCRIBER_INFO: 1613 /* TODO should we handle type as well? */ 1614 mSubscriberInfo = event.valueString; 1615 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1616 intent.putExtra(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, 1617 mSubscriberInfo); 1618 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1619 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1620 break; 1621 case EVENT_TYPE_LAST_VOICE_TAG_NUMBER: 1622 intent = new Intent(BluetoothHeadsetClient.ACTION_LAST_VTAG); 1623 intent.putExtra(BluetoothHeadsetClient.EXTRA_NUMBER, 1624 event.valueString); 1625 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1626 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1627 break; 1628 case EVENT_TYPE_RING_INDICATION: 1629 // Ringing is not handled at this indication and rather should be 1630 // implemented (by the client of this service). Use the 1631 // CALL_STATE_INCOMING (and similar) handle ringing. 1632 break; 1633 default: 1634 Log.e(TAG, "Unknown stack event: " + event.type); 1635 break; 1636 } 1637 1638 break; 1639 default: 1640 return NOT_HANDLED; 1641 } 1642 return HANDLED; 1643 } 1644 1645 private void sendActionResultIntent(StackEvent event) { 1646 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_RESULT); 1647 intent.putExtra(BluetoothHeadsetClient.EXTRA_RESULT_CODE, event.valueInt); 1648 if (event.valueInt == BluetoothHeadsetClient.ACTION_RESULT_ERROR_CME) { 1649 intent.putExtra(BluetoothHeadsetClient.EXTRA_CME_CODE, event.valueInt2); 1650 } 1651 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1652 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1653 } 1654 1655 // in Connected state 1656 private void processConnectionEvent(int state, BluetoothDevice device) { 1657 switch (state) { 1658 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1659 if (DBG) { 1660 Log.d(TAG, "Connected disconnects."); 1661 } 1662 // AG disconnects 1663 if (mCurrentDevice.equals(device)) { 1664 broadcastConnectionState(mCurrentDevice, 1665 BluetoothProfile.STATE_DISCONNECTED, 1666 BluetoothProfile.STATE_CONNECTED); 1667 mCurrentDevice = null; 1668 transitionTo(mDisconnected); 1669 } else { 1670 Log.e(TAG, "Disconnected from unknown device: " + device); 1671 } 1672 break; 1673 default: 1674 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1675 break; 1676 } 1677 } 1678 1679 // in Connected state 1680 private void processAudioEvent(int state, BluetoothDevice device) { 1681 // message from old device 1682 if (!mCurrentDevice.equals(device)) { 1683 Log.e(TAG, "Audio changed on disconnected device: " + device); 1684 return; 1685 } 1686 1687 switch (state) { 1688 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC: 1689 mAudioWbs = true; 1690 // fall through 1691 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED: 1692 if (!mAudioRouteAllowed) { 1693 sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); 1694 break; 1695 } 1696 1697 // Audio state is split in two parts, the audio focus is maintained by the 1698 // entity exercising this service (typically the Telecom stack) and audio 1699 // routing is handled by the bluetooth stack itself. The only reason to do so is 1700 // because Bluetooth SCO connection from the HF role is not entirely supported 1701 // for routing and volume purposes. 1702 // NOTE: All calls here are routed via the setParameters which changes the 1703 // routing at the Audio HAL level. 1704 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED; 1705 1706 // We need to set the volume after switching into HFP mode as some Audio HALs 1707 // reset the volume to a known-default on mode switch. 1708 final int amVol = 1709 mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1710 final int hfVol = amToHfVol(amVol); 1711 1712 if (DBG) { 1713 Log.d(TAG,"hfp_enable=true mAudioWbs is " + mAudioWbs); 1714 } 1715 if (mAudioWbs) { 1716 if (DBG) { 1717 Log.d(TAG,"Setting sampling rate as 16000"); 1718 } 1719 mAudioManager.setParameters("hfp_set_sampling_rate=16000"); 1720 } 1721 else { 1722 if (DBG) { 1723 Log.d(TAG,"Setting sampling rate as 8000"); 1724 } 1725 mAudioManager.setParameters("hfp_set_sampling_rate=8000"); 1726 } 1727 if (DBG) { 1728 Log.d(TAG, "hf_volume " + hfVol); 1729 } 1730 mAudioManager.setParameters("hfp_enable=true"); 1731 mAudioManager.setParameters("hfp_volume=" + hfVol); 1732 transitionTo(mAudioOn); 1733 break; 1734 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING: 1735 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING; 1736 broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING, 1737 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); 1738 break; 1739 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: 1740 if (mAudioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTING) { 1741 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1742 broadcastAudioState(device, 1743 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1744 BluetoothHeadsetClient.STATE_AUDIO_CONNECTING); 1745 } 1746 break; 1747 default: 1748 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1749 break; 1750 } 1751 } 1752 1753 @Override 1754 public void exit() { 1755 if (DBG) { 1756 Log.d(TAG, "Exit Connected: " + getCurrentMessage().what); 1757 } 1758 } 1759 } 1760 1761 private class AudioOn extends State { 1762 @Override 1763 public void enter() { 1764 if (DBG) { 1765 Log.d(TAG, "Enter AudioOn: " + getCurrentMessage().what); 1766 } 1767 broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_CONNECTED, 1768 BluetoothHeadsetClient.STATE_AUDIO_CONNECTING); 1769 } 1770 1771 @Override 1772 public synchronized boolean processMessage(Message message) { 1773 if (DBG) { 1774 Log.d(TAG, "AudioOn process message: " + message.what); 1775 } 1776 if (DBG) { 1777 if (mCurrentDevice == null) { 1778 Log.e(TAG, "ERROR: mCurrentDevice is null in Connected"); 1779 return NOT_HANDLED; 1780 } 1781 } 1782 1783 switch (message.what) { 1784 case DISCONNECT: 1785 BluetoothDevice device = (BluetoothDevice) message.obj; 1786 if (!mCurrentDevice.equals(device)) { 1787 break; 1788 } 1789 deferMessage(message); 1790 /* 1791 * fall through - disconnect audio first then expect 1792 * deferred DISCONNECT message in Connected state 1793 */ 1794 case DISCONNECT_AUDIO: 1795 /* 1796 * just disconnect audio and wait for 1797 * EVENT_TYPE_AUDIO_STATE_CHANGED, that triggers State 1798 * Machines state changing 1799 */ 1800 if (disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1801 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1802 if (DBG) { 1803 Log.d(TAG,"hfp_enable=false"); 1804 } 1805 mAudioManager.setParameters("hfp_enable=false"); 1806 broadcastAudioState(mCurrentDevice, 1807 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1808 BluetoothHeadsetClient.STATE_AUDIO_CONNECTED); 1809 } 1810 break; 1811 case STACK_EVENT: 1812 StackEvent event = (StackEvent) message.obj; 1813 if (DBG) { 1814 Log.d(TAG, "AudioOn: event type: " + event.type); 1815 } 1816 switch (event.type) { 1817 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1818 if (DBG) { 1819 Log.d(TAG, "AudioOn connection state changed" + event.device + ": " 1820 + event.valueInt); 1821 } 1822 processConnectionEvent(event.valueInt, event.device); 1823 break; 1824 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1825 if (DBG) { 1826 Log.d(TAG, "AudioOn audio state changed" + event.device + ": " 1827 + event.valueInt); 1828 } 1829 processAudioEvent(event.valueInt, event.device); 1830 break; 1831 default: 1832 return NOT_HANDLED; 1833 } 1834 break; 1835 default: 1836 return NOT_HANDLED; 1837 } 1838 return HANDLED; 1839 } 1840 1841 // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this 1842 private void processConnectionEvent(int state, BluetoothDevice device) { 1843 switch (state) { 1844 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1845 if (mCurrentDevice.equals(device)) { 1846 processAudioEvent(HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED, 1847 device); 1848 broadcastConnectionState(mCurrentDevice, 1849 BluetoothProfile.STATE_DISCONNECTED, 1850 BluetoothProfile.STATE_CONNECTED); 1851 mCurrentDevice = null; 1852 transitionTo(mDisconnected); 1853 } else { 1854 Log.e(TAG, "Disconnected from unknown device: " + device); 1855 } 1856 break; 1857 default: 1858 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1859 break; 1860 } 1861 } 1862 1863 // in AudioOn state 1864 private void processAudioEvent(int state, BluetoothDevice device) { 1865 if (!mCurrentDevice.equals(device)) { 1866 Log.e(TAG, "Audio changed on disconnected device: " + device); 1867 return; 1868 } 1869 1870 switch (state) { 1871 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: 1872 if (mAudioState != BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED) { 1873 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1874 // Audio focus may still be held by the entity controlling the actual call 1875 // (such as Telecom) and hence this will still keep the call around, there 1876 // is not much we can do here since dropping the call without user consent 1877 // even if the audio connection snapped may not be a good idea. 1878 if (DBG) { 1879 Log.d(TAG,"hfp_enable=false"); 1880 } 1881 mAudioManager.setParameters("hfp_enable=false"); 1882 broadcastAudioState(device, 1883 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1884 BluetoothHeadsetClient.STATE_AUDIO_CONNECTED); 1885 } 1886 1887 transitionTo(mConnected); 1888 break; 1889 default: 1890 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1891 break; 1892 } 1893 } 1894 1895 @Override 1896 public void exit() { 1897 if (DBG) { 1898 Log.d(TAG, "Exit AudioOn: " + getCurrentMessage().what); 1899 } 1900 } 1901 } 1902 1903 /** 1904 * @hide 1905 */ 1906 public synchronized int getConnectionState(BluetoothDevice device) { 1907 if (mCurrentDevice == null) { 1908 return BluetoothProfile.STATE_DISCONNECTED; 1909 } 1910 1911 if (!mCurrentDevice.equals(device)) { 1912 return BluetoothProfile.STATE_DISCONNECTED; 1913 } 1914 1915 IState currentState = getCurrentState(); 1916 if (currentState == mConnecting) { 1917 return BluetoothProfile.STATE_CONNECTING; 1918 } 1919 1920 if (currentState == mConnected || currentState == mAudioOn) { 1921 return BluetoothProfile.STATE_CONNECTED; 1922 } 1923 1924 Log.e(TAG, "Bad currentState: " + currentState); 1925 return BluetoothProfile.STATE_DISCONNECTED; 1926 } 1927 1928 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 1929 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED); 1930 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1931 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1932 1933 if (newState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) { 1934 intent.putExtra(BluetoothHeadsetClient.EXTRA_AUDIO_WBS, mAudioWbs); 1935 } 1936 1937 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1938 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1939 if (DBG) { 1940 Log.d(TAG, "Audio state " + device + ": " + prevState + "->" + newState); 1941 } 1942 } 1943 1944 // This method does not check for error condition (newState == prevState) 1945 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 1946 if (DBG) { 1947 Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState); 1948 } 1949 /* 1950 * Notifying the connection state change of the profile before sending 1951 * the intent for connection state change, as it was causing a race 1952 * condition, with the UI not being updated with the correct connection 1953 * state. 1954 */ 1955 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET_CLIENT, 1956 newState, prevState); 1957 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); 1958 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1959 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1960 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1961 1962 // add feature extras when connected 1963 if (newState == BluetoothProfile.STATE_CONNECTED) { 1964 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY) == 1965 HeadsetClientHalConstants.PEER_FEAT_3WAY) { 1966 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true); 1967 } 1968 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC) == 1969 HeadsetClientHalConstants.PEER_FEAT_VREC) { 1970 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true); 1971 } 1972 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VTAG) == 1973 HeadsetClientHalConstants.PEER_FEAT_VTAG) { 1974 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT, true); 1975 } 1976 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT) == 1977 HeadsetClientHalConstants.PEER_FEAT_REJECT) { 1978 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true); 1979 } 1980 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC) == 1981 HeadsetClientHalConstants.PEER_FEAT_ECC) { 1982 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true); 1983 } 1984 1985 // add individual CHLD support extras 1986 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) == 1987 HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) { 1988 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true); 1989 } 1990 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL) == 1991 HeadsetClientHalConstants.CHLD_FEAT_REL) { 1992 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true); 1993 } 1994 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) == 1995 HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) { 1996 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true); 1997 } 1998 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE) == 1999 HeadsetClientHalConstants.CHLD_FEAT_MERGE) { 2000 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true); 2001 } 2002 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) == 2003 HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) { 2004 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true); 2005 } 2006 } 2007 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 2008 } 2009 2010 boolean isConnected() { 2011 IState currentState = getCurrentState(); 2012 return (currentState == mConnected || currentState == mAudioOn); 2013 } 2014 2015 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 2016 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 2017 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 2018 int connectionState; 2019 synchronized (this) { 2020 for (BluetoothDevice device : bondedDevices) { 2021 ParcelUuid[] featureUuids = device.getUuids(); 2022 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.Handsfree_AG)) { 2023 continue; 2024 } 2025 connectionState = getConnectionState(device); 2026 for (int state : states) { 2027 if (connectionState == state) { 2028 deviceList.add(device); 2029 } 2030 } 2031 } 2032 } 2033 return deviceList; 2034 } 2035 2036 boolean okToConnect(BluetoothDevice device) { 2037 int priority = mService.getPriority(device); 2038 boolean ret = false; 2039 // check priority and accept or reject the connection. if priority is 2040 // undefined 2041 // it is likely that our SDP has not completed and peer is initiating 2042 // the 2043 // connection. Allow this connection, provided the device is bonded 2044 if ((BluetoothProfile.PRIORITY_OFF < priority) || 2045 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 2046 (device.getBondState() != BluetoothDevice.BOND_NONE))) { 2047 ret = true; 2048 } 2049 return ret; 2050 } 2051 2052 boolean isAudioOn() { 2053 return (getCurrentState() == mAudioOn); 2054 } 2055 2056 public void setAudioRouteAllowed(boolean allowed) { 2057 mAudioRouteAllowed = allowed; 2058 } 2059 2060 public boolean getAudioRouteAllowed() { 2061 return mAudioRouteAllowed; 2062 } 2063 2064 synchronized int getAudioState(BluetoothDevice device) { 2065 if (mCurrentDevice == null || !mCurrentDevice.equals(device)) { 2066 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 2067 } 2068 return mAudioState; 2069 } 2070 2071 /** 2072 * @hide 2073 */ 2074 List<BluetoothDevice> getConnectedDevices() { 2075 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 2076 synchronized (this) { 2077 if (isConnected()) { 2078 devices.add(mCurrentDevice); 2079 } 2080 } 2081 return devices; 2082 } 2083 2084 private BluetoothDevice getDevice(byte[] address) { 2085 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 2086 } 2087 2088 private void onConnectionStateChanged(int state, int peer_feat, int chld_feat, byte[] address) { 2089 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 2090 event.valueInt = state; 2091 event.valueInt2 = peer_feat; 2092 event.valueInt3 = chld_feat; 2093 event.device = getDevice(address); 2094 if (DBG) { 2095 Log.d(TAG, "incoming" + event); 2096 } 2097 sendMessage(STACK_EVENT, event); 2098 } 2099 2100 private void onAudioStateChanged(int state, byte[] address) { 2101 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 2102 event.valueInt = state; 2103 event.device = getDevice(address); 2104 if (DBG) { 2105 Log.d(TAG, "incoming" + event); 2106 } 2107 sendMessage(STACK_EVENT, event); 2108 } 2109 2110 private void onVrStateChanged(int state) { 2111 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 2112 event.valueInt = state; 2113 if (DBG) { 2114 Log.d(TAG, "incoming" + event); 2115 } 2116 sendMessage(STACK_EVENT, event); 2117 } 2118 2119 private void onNetworkState(int state) { 2120 StackEvent event = new StackEvent(EVENT_TYPE_NETWORK_STATE); 2121 event.valueInt = state; 2122 if (DBG) { 2123 Log.d(TAG, "incoming" + event); 2124 } 2125 sendMessage(STACK_EVENT, event); 2126 } 2127 2128 private void onNetworkRoaming(int state) { 2129 StackEvent event = new StackEvent(EVENT_TYPE_ROAMING_STATE); 2130 event.valueInt = state; 2131 if (DBG) { 2132 Log.d(TAG, "incoming" + event); 2133 } 2134 sendMessage(STACK_EVENT, event); 2135 } 2136 2137 private void onNetworkSignal(int signal) { 2138 StackEvent event = new StackEvent(EVENT_TYPE_NETWORK_SIGNAL); 2139 event.valueInt = signal; 2140 if (DBG) { 2141 Log.d(TAG, "incoming" + event); 2142 } 2143 sendMessage(STACK_EVENT, event); 2144 } 2145 2146 private void onBatteryLevel(int level) { 2147 StackEvent event = new StackEvent(EVENT_TYPE_BATTERY_LEVEL); 2148 event.valueInt = level; 2149 if (DBG) { 2150 Log.d(TAG, "incoming" + event); 2151 } 2152 sendMessage(STACK_EVENT, event); 2153 } 2154 2155 private void onCurrentOperator(String name) { 2156 StackEvent event = new StackEvent(EVENT_TYPE_OPERATOR_NAME); 2157 event.valueString = name; 2158 if (DBG) { 2159 Log.d(TAG, "incoming" + event); 2160 } 2161 sendMessage(STACK_EVENT, event); 2162 } 2163 2164 /** 2165 * CIEV (Call indicators) notifying if a call is in progress. 2166 * 2167 * Values Include: 2168 * 0 - No call in progress 2169 * 1 - Atleast 1 call is in progress 2170 */ 2171 private void onCall(int call) { 2172 StackEvent event = new StackEvent(EVENT_TYPE_CALL); 2173 event.valueInt = call; 2174 if (DBG) { 2175 Log.d(TAG, "incoming" + event); 2176 } 2177 sendMessage(STACK_EVENT, event); 2178 } 2179 2180 /** 2181 * CIEV (Call indicators) notifying if call(s) are getting set up. 2182 * 2183 * Values incldue: 2184 * 0 - No current call is in setup 2185 * 1 - Incoming call process ongoing 2186 * 2 - Outgoing call process ongoing 2187 * 3 - Remote party being alerted for outgoing call 2188 */ 2189 private void onCallSetup(int callsetup) { 2190 StackEvent event = new StackEvent(EVENT_TYPE_CALLSETUP); 2191 event.valueInt = callsetup; 2192 if (DBG) { 2193 Log.d(TAG, "incoming" + event); 2194 } 2195 sendMessage(STACK_EVENT, event); 2196 } 2197 2198 /** 2199 * CIEV (Call indicators) notifying call held states. 2200 * 2201 * Values include: 2202 * 0 - No calls held 2203 * 1 - Call is placed on hold or active/held calls wapped (The AG has both an ACTIVE and HELD 2204 * call) 2205 * 2 - Call on hold, no active call 2206 */ 2207 private void onCallHeld(int callheld) { 2208 StackEvent event = new StackEvent(EVENT_TYPE_CALLHELD); 2209 event.valueInt = callheld; 2210 if (DBG) { 2211 Log.d(TAG, "incoming" + event); 2212 } 2213 sendMessage(STACK_EVENT, event); 2214 } 2215 2216 private void onRespAndHold(int resp_and_hold) { 2217 StackEvent event = new StackEvent(EVENT_TYPE_RESP_AND_HOLD); 2218 event.valueInt = resp_and_hold; 2219 if (DBG) { 2220 Log.d(TAG, "incoming" + event); 2221 } 2222 sendMessage(STACK_EVENT, event); 2223 } 2224 2225 private void onClip(String number) { 2226 StackEvent event = new StackEvent(EVENT_TYPE_CLIP); 2227 event.valueString = number; 2228 if (DBG) { 2229 Log.d(TAG, "incoming" + event); 2230 } 2231 sendMessage(STACK_EVENT, event); 2232 } 2233 2234 private void onCallWaiting(String number) { 2235 StackEvent event = new StackEvent(EVENT_TYPE_CALL_WAITING); 2236 event.valueString = number; 2237 if (DBG) { 2238 Log.d(TAG, "incoming" + event); 2239 } 2240 sendMessage(STACK_EVENT, event); 2241 } 2242 2243 private void onCurrentCalls(int index, int dir, int state, int mparty, String number) { 2244 StackEvent event = new StackEvent(EVENT_TYPE_CURRENT_CALLS); 2245 event.valueInt = index; 2246 event.valueInt2 = dir; 2247 event.valueInt3 = state; 2248 event.valueInt4 = mparty; 2249 event.valueString = number; 2250 if (DBG) { 2251 Log.d(TAG, "incoming" + event); 2252 } 2253 sendMessage(STACK_EVENT, event); 2254 } 2255 2256 private void onVolumeChange(int type, int volume) { 2257 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 2258 event.valueInt = type; 2259 event.valueInt2 = volume; 2260 if (DBG) { 2261 Log.d(TAG, "incoming" + event); 2262 } 2263 sendMessage(STACK_EVENT, event); 2264 } 2265 2266 private void onCmdResult(int type, int cme) { 2267 StackEvent event = new StackEvent(EVENT_TYPE_CMD_RESULT); 2268 event.valueInt = type; 2269 event.valueInt2 = cme; 2270 if (DBG) { 2271 Log.d(TAG, "incoming" + event); 2272 } 2273 sendMessage(STACK_EVENT, event); 2274 } 2275 2276 private void onSubscriberInfo(String number, int type) { 2277 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_INFO); 2278 event.valueInt = type; 2279 event.valueString = number; 2280 if (DBG) { 2281 Log.d(TAG, "incoming" + event); 2282 } 2283 sendMessage(STACK_EVENT, event); 2284 } 2285 2286 private void onInBandRing(int in_band) { 2287 StackEvent event = new StackEvent(EVENT_TYPE_IN_BAND_RING); 2288 event.valueInt = in_band; 2289 if (DBG) { 2290 Log.d(TAG, "incoming" + event); 2291 } 2292 sendMessage(STACK_EVENT, event); 2293 } 2294 2295 private void onLastVoiceTagNumber(String number) { 2296 StackEvent event = new StackEvent(EVENT_TYPE_LAST_VOICE_TAG_NUMBER); 2297 event.valueString = number; 2298 if (DBG) { 2299 Log.d(TAG, "incoming" + event); 2300 } 2301 sendMessage(STACK_EVENT, event); 2302 } 2303 2304 private void onRingIndication() { 2305 StackEvent event = new StackEvent(EVENT_TYPE_RING_INDICATION); 2306 if (DBG) { 2307 Log.d(TAG, "incoming" + event); 2308 } 2309 sendMessage(STACK_EVENT, event); 2310 } 2311 2312 private String getCurrentDeviceName() { 2313 String defaultName = "<unknown>"; 2314 if (mCurrentDevice == null) { 2315 return defaultName; 2316 } 2317 String deviceName = mCurrentDevice.getName(); 2318 if (deviceName == null) { 2319 return defaultName; 2320 } 2321 return deviceName; 2322 } 2323 2324 private byte[] getByteAddress(BluetoothDevice device) { 2325 return Utils.getBytesFromAddress(device.getAddress()); 2326 } 2327 2328 // Event types for STACK_EVENT message 2329 final private static int EVENT_TYPE_NONE = 0; 2330 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 2331 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 2332 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 2333 final private static int EVENT_TYPE_NETWORK_STATE = 4; 2334 final private static int EVENT_TYPE_ROAMING_STATE = 5; 2335 final private static int EVENT_TYPE_NETWORK_SIGNAL = 6; 2336 final private static int EVENT_TYPE_BATTERY_LEVEL = 7; 2337 final private static int EVENT_TYPE_OPERATOR_NAME = 8; 2338 final private static int EVENT_TYPE_CALL = 9; 2339 final private static int EVENT_TYPE_CALLSETUP = 10; 2340 final private static int EVENT_TYPE_CALLHELD = 11; 2341 final private static int EVENT_TYPE_CLIP = 12; 2342 final private static int EVENT_TYPE_CALL_WAITING = 13; 2343 final private static int EVENT_TYPE_CURRENT_CALLS = 14; 2344 final private static int EVENT_TYPE_VOLUME_CHANGED = 15; 2345 final private static int EVENT_TYPE_CMD_RESULT = 16; 2346 final private static int EVENT_TYPE_SUBSCRIBER_INFO = 17; 2347 final private static int EVENT_TYPE_RESP_AND_HOLD = 18; 2348 final private static int EVENT_TYPE_IN_BAND_RING = 19; 2349 final private static int EVENT_TYPE_LAST_VOICE_TAG_NUMBER = 20; 2350 final private static int EVENT_TYPE_RING_INDICATION= 21; 2351 2352 // for debugging only 2353 private final String EVENT_TYPE_NAMES[] = 2354 { 2355 "EVENT_TYPE_NONE", 2356 "EVENT_TYPE_CONNECTION_STATE_CHANGED", 2357 "EVENT_TYPE_AUDIO_STATE_CHANGED", 2358 "EVENT_TYPE_VR_STATE_CHANGED", 2359 "EVENT_TYPE_NETWORK_STATE", 2360 "EVENT_TYPE_ROAMING_STATE", 2361 "EVENT_TYPE_NETWORK_SIGNAL", 2362 "EVENT_TYPE_BATTERY_LEVEL", 2363 "EVENT_TYPE_OPERATOR_NAME", 2364 "EVENT_TYPE_CALL", 2365 "EVENT_TYPE_CALLSETUP", 2366 "EVENT_TYPE_CALLHELD", 2367 "EVENT_TYPE_CLIP", 2368 "EVENT_TYPE_CALL_WAITING", 2369 "EVENT_TYPE_CURRENT_CALLS", 2370 "EVENT_TYPE_VOLUME_CHANGED", 2371 "EVENT_TYPE_CMD_RESULT", 2372 "EVENT_TYPE_SUBSCRIBER_INFO", 2373 "EVENT_TYPE_RESP_AND_HOLD", 2374 "EVENT_TYPE_IN_BAND_RING", 2375 "EVENT_TYPE_LAST_VOICE_TAG_NUMBER", 2376 "EVENT_TYPE_RING_INDICATION", 2377 }; 2378 2379 private class StackEvent { 2380 int type = EVENT_TYPE_NONE; 2381 int valueInt = 0; 2382 int valueInt2 = 0; 2383 int valueInt3 = 0; 2384 int valueInt4 = 0; 2385 String valueString = null; 2386 BluetoothDevice device = null; 2387 2388 private StackEvent(int type) { 2389 this.type = type; 2390 } 2391 2392 @Override 2393 public String toString() { 2394 // event dump 2395 StringBuilder result = new StringBuilder(); 2396 result.append("StackEvent {type:" + EVENT_TYPE_NAMES[type]); 2397 result.append(", value1:" + valueInt); 2398 result.append(", value2:" + valueInt2); 2399 result.append(", value3:" + valueInt3); 2400 result.append(", value4:" + valueInt4); 2401 result.append(", string: \"" + valueString + "\""); 2402 result.append(", device:" + device + "}"); 2403 return result.toString(); 2404 } 2405 } 2406 2407 private native static void classInitNative(); 2408 2409 private native void initializeNative(); 2410 2411 private native void cleanupNative(); 2412 2413 private native boolean connectNative(byte[] address); 2414 2415 private native boolean disconnectNative(byte[] address); 2416 2417 private native boolean connectAudioNative(byte[] address); 2418 2419 private native boolean disconnectAudioNative(byte[] address); 2420 2421 private native boolean startVoiceRecognitionNative(); 2422 2423 private native boolean stopVoiceRecognitionNative(); 2424 2425 private native boolean setVolumeNative(int volumeType, int volume); 2426 2427 private native boolean dialNative(String number); 2428 2429 private native boolean dialMemoryNative(int location); 2430 2431 private native boolean handleCallActionNative(int action, int index); 2432 2433 private native boolean queryCurrentCallsNative(); 2434 2435 private native boolean queryCurrentOperatorNameNative(); 2436 2437 private native boolean retrieveSubscriberInfoNative(); 2438 2439 private native boolean sendDtmfNative(byte code); 2440 2441 private native boolean requestLastVoiceTagNumberNative(); 2442 2443 private native boolean sendATCmdNative(int ATCmd, int val1, 2444 int val2, String arg); 2445 2446 public List<BluetoothHeadsetClientCall> getCurrentCalls() { 2447 return new ArrayList<BluetoothHeadsetClientCall>(mCalls.values()); 2448 } 2449 2450 public Bundle getCurrentAgEvents() { 2451 Bundle b = new Bundle(); 2452 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, mIndicatorNetworkState); 2453 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, mIndicatorNetworkSignal); 2454 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, mIndicatorNetworkType); 2455 b.putInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, mIndicatorBatteryLevel); 2456 b.putString(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName); 2457 b.putInt(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, mVoiceRecognitionActive); 2458 b.putInt(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, mInBandRingtone); 2459 b.putString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo); 2460 return b; 2461 } 2462} 2463