HeadsetClientStateMachine.java revision 413273881431c8e88e81e6f4e8e969938d6d29e0
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 = 10000; // 10s 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 if (mAudioManager != null) { 865 mAudioManager.setParameters("hfp_enable=false"); 866 } 867 quitNow(); 868 } 869 870 public void cleanup() { 871 if (mNativeAvailable) { 872 cleanupNative(); 873 mNativeAvailable = false; 874 } 875 } 876 877 private int hfToAmVol(int hfVol) { 878 int amRange = mMaxAmVcVol - mMinAmVcVol; 879 int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME; 880 int amOffset = 881 (amRange * (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)) / hfRange; 882 int amVol = mMinAmVcVol + amOffset; 883 Log.d(TAG, "HF -> AM " + hfVol + " " + amVol); 884 return amVol; 885 } 886 887 private int amToHfVol(int amVol) { 888 int amRange = mMaxAmVcVol - mMinAmVcVol; 889 int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME; 890 int hfOffset = (hfRange * (amVol - mMinAmVcVol)) / amRange; 891 int hfVol = MIN_HFP_SCO_VOICE_CALL_VOLUME + hfOffset; 892 Log.d(TAG, "AM -> HF " + amVol + " " + hfVol); 893 return hfVol; 894 } 895 896 private class Disconnected extends State { 897 @Override 898 public void enter() { 899 Log.d(TAG, "Enter Disconnected: " + getCurrentMessage().what); 900 901 // cleanup 902 mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE; 903 mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME; 904 mIndicatorNetworkSignal = 0; 905 mIndicatorBatteryLevel = 0; 906 907 mAudioWbs = false; 908 909 // will be set on connect 910 mIndicatorCall = -1; 911 mIndicatorCallSetup = -1; 912 mIndicatorCallHeld = -1; 913 914 mOperatorName = null; 915 mSubscriberInfo = null; 916 917 mQueuedActions = new LinkedList<Pair<Integer, Object>>(); 918 clearPendingAction(); 919 920 mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED; 921 mInBandRingtone = HeadsetClientHalConstants.IN_BAND_RING_NOT_PROVIDED; 922 923 mCurrentDevice = null; 924 925 mCalls.clear(); 926 mCallsUpdate.clear(); 927 928 mPeerFeatures = 0; 929 mChldFeatures = 0; 930 931 removeMessages(QUERY_CURRENT_CALLS); 932 } 933 934 @Override 935 public synchronized boolean processMessage(Message message) { 936 Log.d(TAG, "Disconnected process message: " + message.what); 937 938 if (mCurrentDevice != null) { 939 Log.e(TAG, "ERROR: current device not null in Disconnected"); 940 return NOT_HANDLED; 941 } 942 943 switch (message.what) { 944 case CONNECT: 945 BluetoothDevice device = (BluetoothDevice) message.obj; 946 947 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 948 BluetoothProfile.STATE_DISCONNECTED); 949 950 if (!connectNative(getByteAddress(device))) { 951 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 952 BluetoothProfile.STATE_CONNECTING); 953 break; 954 } 955 956 mCurrentDevice = device; 957 958 transitionTo(mConnecting); 959 break; 960 case DISCONNECT: 961 // ignore 962 break; 963 case STACK_EVENT: 964 StackEvent event = (StackEvent) message.obj; 965 if (DBG) { 966 Log.d(TAG, "Stack event type: " + event.type); 967 } 968 switch (event.type) { 969 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 970 if (DBG) { 971 Log.d(TAG, "Disconnected: Connection " + event.device 972 + " state changed:" + event.valueInt); 973 } 974 processConnectionEvent(event.valueInt, event.device); 975 break; 976 default: 977 Log.e(TAG, "Disconnected: Unexpected stack event: " + event.type); 978 break; 979 } 980 break; 981 default: 982 return NOT_HANDLED; 983 } 984 return HANDLED; 985 } 986 987 // in Disconnected state 988 private void processConnectionEvent(int state, BluetoothDevice device) 989 { 990 switch (state) { 991 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED: 992 Log.w(TAG, "HFPClient Connecting from Disconnected state"); 993 if (okToConnect(device)) { 994 Log.i(TAG, "Incoming AG accepted"); 995 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 996 BluetoothProfile.STATE_DISCONNECTED); 997 mCurrentDevice = device; 998 transitionTo(mConnecting); 999 } else { 1000 Log.i(TAG, "Incoming AG rejected. priority=" + mService.getPriority(device) 1001 + 1002 " bondState=" + device.getBondState()); 1003 // reject the connection and stay in Disconnected state 1004 // itself 1005 disconnectNative(getByteAddress(device)); 1006 // the other profile connection should be initiated 1007 AdapterService adapterService = AdapterService.getAdapterService(); 1008 if (adapterService != null) { 1009 adapterService.connectOtherProfile(device, 1010 AdapterService.PROFILE_CONN_REJECTED); 1011 } 1012 } 1013 break; 1014 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING: 1015 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1016 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING: 1017 default: 1018 Log.i(TAG, "ignoring state: " + state); 1019 break; 1020 } 1021 } 1022 1023 @Override 1024 public void exit() { 1025 if (DBG) { 1026 Log.d(TAG, "Exit Disconnected: " + getCurrentMessage().what); 1027 } 1028 } 1029 } 1030 1031 private class Connecting extends State { 1032 @Override 1033 public void enter() { 1034 if (DBG) { 1035 Log.d(TAG, "Enter Connecting: " + getCurrentMessage().what); 1036 } 1037 // This message is either consumed in processMessage or 1038 // removed in exit. It is safe to send a CONNECTING_TIMEOUT here since 1039 // the only transition is when connection attempt is initiated. 1040 sendMessageDelayed(CONNECTING_TIMEOUT, CONNECTING_TIMEOUT_MS); 1041 } 1042 1043 @Override 1044 public synchronized boolean processMessage(Message message) { 1045 if (DBG) { 1046 Log.d(TAG, "Connecting process message: " + message.what); 1047 } 1048 1049 switch (message.what) { 1050 case CONNECT: 1051 case CONNECT_AUDIO: 1052 case DISCONNECT: 1053 deferMessage(message); 1054 break; 1055 case STACK_EVENT: 1056 StackEvent event = (StackEvent) message.obj; 1057 if (DBG) { 1058 Log.d(TAG, "Connecting: event type: " + event.type); 1059 } 1060 switch (event.type) { 1061 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1062 if (DBG) { 1063 Log.d(TAG, "Connecting: Connection " + event.device + " state changed:" 1064 + event.valueInt); 1065 } 1066 processConnectionEvent(event.valueInt, event.valueInt2, 1067 event.valueInt3, event.device); 1068 break; 1069 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1070 case EVENT_TYPE_VR_STATE_CHANGED: 1071 case EVENT_TYPE_NETWORK_STATE: 1072 case EVENT_TYPE_ROAMING_STATE: 1073 case EVENT_TYPE_NETWORK_SIGNAL: 1074 case EVENT_TYPE_BATTERY_LEVEL: 1075 case EVENT_TYPE_CALL: 1076 case EVENT_TYPE_CALLSETUP: 1077 case EVENT_TYPE_CALLHELD: 1078 case EVENT_TYPE_RESP_AND_HOLD: 1079 case EVENT_TYPE_CLIP: 1080 case EVENT_TYPE_CALL_WAITING: 1081 case EVENT_TYPE_VOLUME_CHANGED: 1082 case EVENT_TYPE_IN_BAND_RING: 1083 deferMessage(message); 1084 break; 1085 case EVENT_TYPE_CMD_RESULT: 1086 case EVENT_TYPE_SUBSCRIBER_INFO: 1087 case EVENT_TYPE_CURRENT_CALLS: 1088 case EVENT_TYPE_OPERATOR_NAME: 1089 default: 1090 Log.e(TAG, "Connecting: ignoring stack event: " + event.type); 1091 break; 1092 } 1093 break; 1094 case CONNECTING_TIMEOUT: 1095 // We timed out trying to connect, transition to disconnected. 1096 Log.w(TAG, "Connection timeout for " + mCurrentDevice); 1097 transitionTo(mDisconnected); 1098 broadcastConnectionState( 1099 mCurrentDevice, 1100 BluetoothProfile.STATE_CONNECTING, 1101 BluetoothProfile.STATE_DISCONNECTED); 1102 break; 1103 1104 default: 1105 Log.w(TAG, "Message not handled " + message); 1106 return NOT_HANDLED; 1107 } 1108 return HANDLED; 1109 } 1110 1111 // in Connecting state 1112 private void processConnectionEvent( 1113 int state, int peer_feat, int chld_feat, BluetoothDevice device) { 1114 switch (state) { 1115 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1116 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 1117 BluetoothProfile.STATE_CONNECTING); 1118 transitionTo(mDisconnected); 1119 break; 1120 1121 case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED: 1122 Log.d(TAG, "HFPClient Connected from Connecting state"); 1123 1124 mPeerFeatures = peer_feat; 1125 mChldFeatures = chld_feat; 1126 1127 // We do not support devices which do not support enhanced call status (ECS). 1128 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECS) == 0) { 1129 disconnectNative(getByteAddress(device)); 1130 return; 1131 } 1132 1133 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 1134 BluetoothProfile.STATE_CONNECTING); 1135 1136 // Send AT+NREC to remote if supported by audio 1137 if (HeadsetClientHalConstants.HANDSFREECLIENT_NREC_SUPPORTED && 1138 ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECNR) == 1139 HeadsetClientHalConstants.PEER_FEAT_ECNR)) { 1140 if (sendATCmdNative(HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_NREC, 1141 1 , 0, null)) { 1142 addQueuedAction(DISABLE_NREC); 1143 } else { 1144 Log.e(TAG, "Failed to send NREC"); 1145 } 1146 } 1147 transitionTo(mConnected); 1148 1149 int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1150 sendMessage( 1151 obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0)); 1152 // Mic is either in ON state (full volume) or OFF state. There is no way in 1153 // Android to change the MIC volume. 1154 sendMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME, 1155 mAudioManager.isMicrophoneMute() ? 0 : 15, 0)); 1156 1157 // query subscriber info 1158 sendMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO); 1159 break; 1160 1161 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED: 1162 if (!mCurrentDevice.equals(device)) { 1163 Log.w(TAG, "incoming connection event, device: " + device); 1164 1165 broadcastConnectionState(mCurrentDevice, 1166 BluetoothProfile.STATE_DISCONNECTED, 1167 BluetoothProfile.STATE_CONNECTING); 1168 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 1169 BluetoothProfile.STATE_DISCONNECTED); 1170 1171 mCurrentDevice = device; 1172 } 1173 break; 1174 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING: 1175 /* outgoing connecting started */ 1176 if (DBG) { 1177 Log.d(TAG, "outgoing connection started, ignore"); 1178 } 1179 break; 1180 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING: 1181 default: 1182 Log.e(TAG, "Incorrect state: " + state); 1183 break; 1184 } 1185 } 1186 1187 @Override 1188 public void exit() { 1189 if (DBG) { 1190 Log.d(TAG, "Exit Connecting: " + getCurrentMessage().what); 1191 } 1192 removeMessages(CONNECTING_TIMEOUT); 1193 } 1194 } 1195 1196 private class Connected extends State { 1197 @Override 1198 public void enter() { 1199 if (DBG) { 1200 Log.d(TAG, "Enter Connected: " + getCurrentMessage().what); 1201 } 1202 mAudioWbs = false; 1203 } 1204 1205 @Override 1206 public synchronized boolean processMessage(Message message) { 1207 if (DBG) { 1208 Log.d(TAG, "Connected process message: " + message.what); 1209 } 1210 if (DBG) { 1211 if (mCurrentDevice == null) { 1212 Log.e(TAG, "ERROR: mCurrentDevice is null in Connected"); 1213 return NOT_HANDLED; 1214 } 1215 } 1216 1217 switch (message.what) { 1218 case CONNECT: 1219 BluetoothDevice device = (BluetoothDevice) message.obj; 1220 if (mCurrentDevice.equals(device)) { 1221 // already connected to this device, do nothing 1222 break; 1223 } 1224 1225 if (!disconnectNative(getByteAddress(mCurrentDevice))) { 1226 // if succeed this will be handled from disconnected 1227 // state 1228 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 1229 BluetoothProfile.STATE_DISCONNECTED); 1230 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 1231 BluetoothProfile.STATE_CONNECTING); 1232 break; 1233 } 1234 1235 // will be handled when entered disconnected 1236 deferMessage(message); 1237 break; 1238 case DISCONNECT: 1239 BluetoothDevice dev = (BluetoothDevice) message.obj; 1240 if (!mCurrentDevice.equals(dev)) { 1241 break; 1242 } 1243 broadcastConnectionState(dev, BluetoothProfile.STATE_DISCONNECTING, 1244 BluetoothProfile.STATE_CONNECTED); 1245 if (!disconnectNative(getByteAddress(dev))) { 1246 // disconnecting failed 1247 broadcastConnectionState(dev, BluetoothProfile.STATE_CONNECTED, 1248 BluetoothProfile.STATE_DISCONNECTED); 1249 break; 1250 } 1251 break; 1252 case CONNECT_AUDIO: 1253 // TODO: handle audio connection failure 1254 if (!connectAudioNative(getByteAddress(mCurrentDevice))) { 1255 Log.e(TAG, "ERROR: Couldn't connect Audio."); 1256 } 1257 break; 1258 case DISCONNECT_AUDIO: 1259 // TODO: handle audio disconnection failure 1260 if (!disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1261 Log.e(TAG, "ERROR: Couldn't connect Audio."); 1262 } 1263 break; 1264 case VOICE_RECOGNITION_START: 1265 if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STOPPED) { 1266 if (startVoiceRecognitionNative()) { 1267 addQueuedAction(VOICE_RECOGNITION_START); 1268 } else { 1269 Log.e(TAG, "ERROR: Couldn't start voice recognition"); 1270 } 1271 } 1272 break; 1273 case VOICE_RECOGNITION_STOP: 1274 if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STARTED) { 1275 if (stopVoiceRecognitionNative()) { 1276 addQueuedAction(VOICE_RECOGNITION_STOP); 1277 } else { 1278 Log.e(TAG, "ERROR: Couldn't stop voice recognition"); 1279 } 1280 } 1281 break; 1282 // Called only for Mute/Un-mute - Mic volume change is not allowed. 1283 case SET_MIC_VOLUME: 1284 if (mVgmFromStack) { 1285 mVgmFromStack = false; 1286 break; 1287 } 1288 if (setVolumeNative(HeadsetClientHalConstants.VOLUME_TYPE_MIC, message.arg1)) { 1289 addQueuedAction(SET_MIC_VOLUME); 1290 } 1291 break; 1292 case SET_SPEAKER_VOLUME: 1293 // This message should always contain the volume in AudioManager max normalized. 1294 int amVol = message.arg1; 1295 int hfVol = amToHfVol(amVol); 1296 Log.d(TAG,"HF volume is set to " + hfVol); 1297 mAudioManager.setParameters("hfp_volume=" + hfVol); 1298 if (mVgsFromStack) { 1299 mVgsFromStack = false; 1300 break; 1301 } 1302 if (setVolumeNative(HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) { 1303 addQueuedAction(SET_SPEAKER_VOLUME); 1304 } 1305 break; 1306 case DIAL_NUMBER: 1307 // Add the call as an outgoing call. 1308 BluetoothHeadsetClientCall c = (BluetoothHeadsetClientCall) message.obj; 1309 mCalls.put(HF_ORIGINATED_CALL_ID, c); 1310 1311 if (dialNative(c.getNumber())) { 1312 addQueuedAction(DIAL_NUMBER, c.getNumber()); 1313 // Start looping on calling current calls. 1314 sendMessage(QUERY_CURRENT_CALLS); 1315 } else { 1316 Log.e(TAG, "ERROR: Cannot dial with a given number:" + (String) message.obj); 1317 // Set the call to terminated remove. 1318 c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED); 1319 sendCallChangedIntent(c); 1320 mCalls.remove(HF_ORIGINATED_CALL_ID); 1321 } 1322 break; 1323 case ACCEPT_CALL: 1324 acceptCall(message.arg1, false); 1325 break; 1326 case REJECT_CALL: 1327 rejectCall(); 1328 break; 1329 case HOLD_CALL: 1330 holdCall(); 1331 break; 1332 case TERMINATE_CALL: 1333 terminateCall(); 1334 break; 1335 case ENTER_PRIVATE_MODE: 1336 enterPrivateMode(message.arg1); 1337 break; 1338 case EXPLICIT_CALL_TRANSFER: 1339 explicitCallTransfer(); 1340 break; 1341 case SEND_DTMF: 1342 if (sendDtmfNative((byte) message.arg1)) { 1343 addQueuedAction(SEND_DTMF); 1344 } else { 1345 Log.e(TAG, "ERROR: Couldn't send DTMF"); 1346 } 1347 break; 1348 case SUBSCRIBER_INFO: 1349 if (retrieveSubscriberInfoNative()) { 1350 addQueuedAction(SUBSCRIBER_INFO); 1351 } else { 1352 Log.e(TAG, "ERROR: Couldn't retrieve subscriber info"); 1353 } 1354 break; 1355 case LAST_VTAG_NUMBER: 1356 if (requestLastVoiceTagNumberNative()) { 1357 addQueuedAction(LAST_VTAG_NUMBER); 1358 } else { 1359 Log.e(TAG, "ERROR: Couldn't get last VTAG number"); 1360 } 1361 break; 1362 case QUERY_CURRENT_CALLS: 1363 // Whenever the timer expires we query calls if there are outstanding requests 1364 // for query calls. 1365 long currentElapsed = SystemClock.elapsedRealtime(); 1366 if (mClccTimer < currentElapsed) { 1367 queryCallsStart(); 1368 mClccTimer = currentElapsed + QUERY_CURRENT_CALLS_WAIT_MILLIS; 1369 // Request satisfied, ignore all other call query messages. 1370 removeMessages(QUERY_CURRENT_CALLS); 1371 } else { 1372 // Replace all messages with one concrete message. 1373 removeMessages(QUERY_CURRENT_CALLS); 1374 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS); 1375 } 1376 break; 1377 case STACK_EVENT: 1378 Intent intent = null; 1379 StackEvent event = (StackEvent) message.obj; 1380 if (DBG) { 1381 Log.d(TAG, "Connected: event type: " + event.type); 1382 } 1383 1384 switch (event.type) { 1385 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1386 if (DBG) { 1387 Log.d(TAG, "Connected: Connection state changed: " + event.device 1388 + ": " + event.valueInt); 1389 } 1390 processConnectionEvent(event.valueInt, event.device); 1391 break; 1392 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1393 if (DBG) { 1394 Log.d(TAG, "Connected: Audio state changed: " + event.device + ": " 1395 + event.valueInt); 1396 } 1397 processAudioEvent(event.valueInt, event.device); 1398 break; 1399 case EVENT_TYPE_NETWORK_STATE: 1400 if (DBG) { 1401 Log.d(TAG, "Connected: Network state: " + event.valueInt); 1402 } 1403 mIndicatorNetworkState = event.valueInt; 1404 1405 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1406 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, 1407 event.valueInt); 1408 1409 if (mIndicatorNetworkState == 1410 HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE) { 1411 mOperatorName = null; 1412 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, 1413 mOperatorName); 1414 } 1415 1416 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1417 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1418 1419 if (mIndicatorNetworkState == 1420 HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE) { 1421 if (queryCurrentOperatorNameNative()) { 1422 addQueuedAction(QUERY_OPERATOR_NAME); 1423 } else { 1424 Log.e(TAG, "ERROR: Couldn't querry operator name"); 1425 } 1426 } 1427 break; 1428 case EVENT_TYPE_ROAMING_STATE: 1429 mIndicatorNetworkType = event.valueInt; 1430 1431 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1432 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, 1433 event.valueInt); 1434 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1435 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1436 break; 1437 case EVENT_TYPE_NETWORK_SIGNAL: 1438 mIndicatorNetworkSignal = event.valueInt; 1439 1440 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1441 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, 1442 event.valueInt); 1443 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1444 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1445 break; 1446 case EVENT_TYPE_BATTERY_LEVEL: 1447 mIndicatorBatteryLevel = event.valueInt; 1448 1449 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1450 intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, 1451 event.valueInt); 1452 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1453 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1454 break; 1455 case EVENT_TYPE_OPERATOR_NAME: 1456 mOperatorName = event.valueString; 1457 1458 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1459 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, 1460 event.valueString); 1461 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1462 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1463 break; 1464 case EVENT_TYPE_VR_STATE_CHANGED: 1465 if (mVoiceRecognitionActive != event.valueInt) { 1466 mVoiceRecognitionActive = event.valueInt; 1467 1468 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1469 intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, 1470 mVoiceRecognitionActive); 1471 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1472 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1473 } 1474 break; 1475 case EVENT_TYPE_CALL: 1476 updateCallIndicator(event.valueInt); 1477 break; 1478 case EVENT_TYPE_CALLSETUP: 1479 updateCallSetupIndicator(event.valueInt); 1480 break; 1481 case EVENT_TYPE_CALLHELD: 1482 updateCallHeldIndicator(event.valueInt); 1483 break; 1484 case EVENT_TYPE_RESP_AND_HOLD: 1485 updateRespAndHold(event.valueInt); 1486 break; 1487 case EVENT_TYPE_CLIP: 1488 updateClip(event.valueString); 1489 break; 1490 case EVENT_TYPE_CALL_WAITING: 1491 updateCCWA(event.valueString); 1492 break; 1493 case EVENT_TYPE_IN_BAND_RING: 1494 if (mInBandRingtone != event.valueInt) { 1495 mInBandRingtone = event.valueInt; 1496 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1497 intent.putExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, 1498 mInBandRingtone); 1499 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1500 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1501 } 1502 break; 1503 case EVENT_TYPE_CURRENT_CALLS: 1504 queryCallsUpdate( 1505 event.valueInt, 1506 event.valueInt3, 1507 event.valueString, 1508 event.valueInt4 == 1509 HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI, 1510 event.valueInt2 == 1511 HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING); 1512 break; 1513 case EVENT_TYPE_VOLUME_CHANGED: 1514 if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) { 1515 Log.d(TAG, "AM volume set to " + 1516 hfToAmVol(event.valueInt2)); 1517 mAudioManager.setStreamVolume( 1518 AudioManager.STREAM_VOICE_CALL, 1519 hfToAmVol(event.valueInt2), 1520 AudioManager.FLAG_SHOW_UI); 1521 mVgsFromStack = true; 1522 } else if (event.valueInt == 1523 HeadsetClientHalConstants.VOLUME_TYPE_MIC) { 1524 mAudioManager.setMicrophoneMute(event.valueInt2 == 0); 1525 1526 mVgmFromStack = true; 1527 } 1528 break; 1529 case EVENT_TYPE_CMD_RESULT: 1530 Pair<Integer, Object> queuedAction = mQueuedActions.poll(); 1531 1532 // should not happen but... 1533 if (queuedAction == null || queuedAction.first == NO_ACTION) { 1534 clearPendingAction(); 1535 break; 1536 } 1537 1538 if (DBG) { 1539 Log.d(TAG, "Connected: command result: " + event.valueInt 1540 + " queuedAction: " + queuedAction.first); 1541 } 1542 1543 switch (queuedAction.first) { 1544 case VOICE_RECOGNITION_STOP: 1545 case VOICE_RECOGNITION_START: 1546 if (event.valueInt == HeadsetClientHalConstants.CMD_COMPLETE_OK) { 1547 if (queuedAction.first == VOICE_RECOGNITION_STOP) { 1548 mVoiceRecognitionActive = 1549 HeadsetClientHalConstants.VR_STATE_STOPPED; 1550 } else { 1551 mVoiceRecognitionActive = 1552 HeadsetClientHalConstants.VR_STATE_STARTED; 1553 } 1554 } 1555 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1556 intent.putExtra( 1557 BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, 1558 mVoiceRecognitionActive); 1559 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1560 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1561 break; 1562 case QUERY_CURRENT_CALLS: 1563 queryCallsDone(); 1564 break; 1565 case ACCEPT_CALL: 1566 if (event.valueInt == BluetoothHeadsetClient.ACTION_RESULT_OK) { 1567 mPendingAction = queuedAction; 1568 } else { 1569 if (callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) == 0) { 1570 if(getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING) != null && 1571 (Integer) mPendingAction.second == HeadsetClientHalConstants.CALL_ACTION_ATA) { 1572 acceptCall(BluetoothHeadsetClient.CALL_ACCEPT_NONE, true); 1573 break; 1574 } else if(getCall(BluetoothHeadsetClientCall.CALL_STATE_WAITING) != null && 1575 (Integer) mPendingAction.second == HeadsetClientHalConstants.CALL_ACTION_CHLD_2) { 1576 acceptCall(BluetoothHeadsetClient.CALL_ACCEPT_NONE, true); 1577 break; 1578 } 1579 } 1580 sendActionResultIntent(event); 1581 } 1582 break; 1583 case DIAL_NUMBER: 1584 case REJECT_CALL: 1585 case HOLD_CALL: 1586 case TERMINATE_CALL: 1587 case ENTER_PRIVATE_MODE: 1588 case TERMINATE_SPECIFIC_CALL: 1589 // if terminating specific succeed no other 1590 // event is send 1591 if (event.valueInt != BluetoothHeadsetClient.ACTION_RESULT_OK) { 1592 sendActionResultIntent(event); 1593 } 1594 break; 1595 case LAST_VTAG_NUMBER: 1596 if (event.valueInt != BluetoothHeadsetClient.ACTION_RESULT_OK) { 1597 sendActionResultIntent(event); 1598 } 1599 break; 1600 case DISABLE_NREC: 1601 if (event.valueInt != HeadsetClientHalConstants.CMD_COMPLETE_OK) { 1602 Log.w(TAG, "Failed to disable AG's EC and NR"); 1603 } 1604 break; 1605 case SET_MIC_VOLUME: 1606 case SET_SPEAKER_VOLUME: 1607 case SUBSCRIBER_INFO: 1608 case QUERY_OPERATOR_NAME: 1609 break; 1610 default: 1611 sendActionResultIntent(event); 1612 break; 1613 } 1614 1615 break; 1616 case EVENT_TYPE_SUBSCRIBER_INFO: 1617 /* TODO should we handle type as well? */ 1618 mSubscriberInfo = event.valueString; 1619 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1620 intent.putExtra(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, 1621 mSubscriberInfo); 1622 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1623 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1624 break; 1625 case EVENT_TYPE_LAST_VOICE_TAG_NUMBER: 1626 intent = new Intent(BluetoothHeadsetClient.ACTION_LAST_VTAG); 1627 intent.putExtra(BluetoothHeadsetClient.EXTRA_NUMBER, 1628 event.valueString); 1629 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1630 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1631 break; 1632 case EVENT_TYPE_RING_INDICATION: 1633 // Ringing is not handled at this indication and rather should be 1634 // implemented (by the client of this service). Use the 1635 // CALL_STATE_INCOMING (and similar) handle ringing. 1636 break; 1637 default: 1638 Log.e(TAG, "Unknown stack event: " + event.type); 1639 break; 1640 } 1641 1642 break; 1643 default: 1644 return NOT_HANDLED; 1645 } 1646 return HANDLED; 1647 } 1648 1649 private void sendActionResultIntent(StackEvent event) { 1650 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_RESULT); 1651 intent.putExtra(BluetoothHeadsetClient.EXTRA_RESULT_CODE, event.valueInt); 1652 if (event.valueInt == BluetoothHeadsetClient.ACTION_RESULT_ERROR_CME) { 1653 intent.putExtra(BluetoothHeadsetClient.EXTRA_CME_CODE, event.valueInt2); 1654 } 1655 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1656 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1657 } 1658 1659 // in Connected state 1660 private void processConnectionEvent(int state, BluetoothDevice device) { 1661 switch (state) { 1662 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1663 if (DBG) { 1664 Log.d(TAG, "Connected disconnects."); 1665 } 1666 // AG disconnects 1667 if (mCurrentDevice.equals(device)) { 1668 broadcastConnectionState(mCurrentDevice, 1669 BluetoothProfile.STATE_DISCONNECTED, 1670 BluetoothProfile.STATE_CONNECTED); 1671 transitionTo(mDisconnected); 1672 } else { 1673 Log.e(TAG, "Disconnected from unknown device: " + device); 1674 } 1675 break; 1676 default: 1677 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1678 break; 1679 } 1680 } 1681 1682 // in Connected state 1683 private void processAudioEvent(int state, BluetoothDevice device) { 1684 // message from old device 1685 if (!mCurrentDevice.equals(device)) { 1686 Log.e(TAG, "Audio changed on disconnected device: " + device); 1687 return; 1688 } 1689 1690 switch (state) { 1691 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC: 1692 mAudioWbs = true; 1693 // fall through 1694 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED: 1695 if (!mAudioRouteAllowed) { 1696 sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); 1697 break; 1698 } 1699 1700 // Audio state is split in two parts, the audio focus is maintained by the 1701 // entity exercising this service (typically the Telecom stack) and audio 1702 // routing is handled by the bluetooth stack itself. The only reason to do so is 1703 // because Bluetooth SCO connection from the HF role is not entirely supported 1704 // for routing and volume purposes. 1705 // NOTE: All calls here are routed via the setParameters which changes the 1706 // routing at the Audio HAL level. 1707 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED; 1708 1709 // We need to set the volume after switching into HFP mode as some Audio HALs 1710 // reset the volume to a known-default on mode switch. 1711 final int amVol = 1712 mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1713 final int hfVol = amToHfVol(amVol); 1714 1715 if (DBG) { 1716 Log.d(TAG,"hfp_enable=true mAudioWbs is " + mAudioWbs); 1717 } 1718 if (mAudioWbs) { 1719 if (DBG) { 1720 Log.d(TAG,"Setting sampling rate as 16000"); 1721 } 1722 mAudioManager.setParameters("hfp_set_sampling_rate=16000"); 1723 } 1724 else { 1725 if (DBG) { 1726 Log.d(TAG,"Setting sampling rate as 8000"); 1727 } 1728 mAudioManager.setParameters("hfp_set_sampling_rate=8000"); 1729 } 1730 if (DBG) { 1731 Log.d(TAG, "hf_volume " + hfVol); 1732 } 1733 mAudioManager.setParameters("hfp_enable=true"); 1734 mAudioManager.setParameters("hfp_volume=" + hfVol); 1735 transitionTo(mAudioOn); 1736 break; 1737 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING: 1738 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING; 1739 broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING, 1740 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); 1741 break; 1742 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: 1743 if (mAudioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTING) { 1744 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1745 broadcastAudioState(device, 1746 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1747 BluetoothHeadsetClient.STATE_AUDIO_CONNECTING); 1748 } 1749 break; 1750 default: 1751 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1752 break; 1753 } 1754 } 1755 1756 @Override 1757 public void exit() { 1758 if (DBG) { 1759 Log.d(TAG, "Exit Connected: " + getCurrentMessage().what); 1760 } 1761 } 1762 } 1763 1764 private class AudioOn extends State { 1765 @Override 1766 public void enter() { 1767 if (DBG) { 1768 Log.d(TAG, "Enter AudioOn: " + getCurrentMessage().what); 1769 } 1770 broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_CONNECTED, 1771 BluetoothHeadsetClient.STATE_AUDIO_CONNECTING); 1772 } 1773 1774 @Override 1775 public synchronized boolean processMessage(Message message) { 1776 if (DBG) { 1777 Log.d(TAG, "AudioOn process message: " + message.what); 1778 } 1779 if (DBG) { 1780 if (mCurrentDevice == null) { 1781 Log.e(TAG, "ERROR: mCurrentDevice is null in Connected"); 1782 return NOT_HANDLED; 1783 } 1784 } 1785 1786 switch (message.what) { 1787 case DISCONNECT: 1788 BluetoothDevice device = (BluetoothDevice) message.obj; 1789 if (!mCurrentDevice.equals(device)) { 1790 break; 1791 } 1792 deferMessage(message); 1793 /* 1794 * fall through - disconnect audio first then expect 1795 * deferred DISCONNECT message in Connected state 1796 */ 1797 case DISCONNECT_AUDIO: 1798 /* 1799 * just disconnect audio and wait for 1800 * EVENT_TYPE_AUDIO_STATE_CHANGED, that triggers State 1801 * Machines state changing 1802 */ 1803 if (disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1804 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1805 if (DBG) { 1806 Log.d(TAG,"hfp_enable=false"); 1807 } 1808 mAudioManager.setParameters("hfp_enable=false"); 1809 broadcastAudioState(mCurrentDevice, 1810 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1811 BluetoothHeadsetClient.STATE_AUDIO_CONNECTED); 1812 } 1813 break; 1814 case STACK_EVENT: 1815 StackEvent event = (StackEvent) message.obj; 1816 if (DBG) { 1817 Log.d(TAG, "AudioOn: event type: " + event.type); 1818 } 1819 switch (event.type) { 1820 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 1821 if (DBG) { 1822 Log.d(TAG, "AudioOn connection state changed" + event.device + ": " 1823 + event.valueInt); 1824 } 1825 processConnectionEvent(event.valueInt, event.device); 1826 break; 1827 case EVENT_TYPE_AUDIO_STATE_CHANGED: 1828 if (DBG) { 1829 Log.d(TAG, "AudioOn audio state changed" + event.device + ": " 1830 + event.valueInt); 1831 } 1832 processAudioEvent(event.valueInt, event.device); 1833 break; 1834 default: 1835 return NOT_HANDLED; 1836 } 1837 break; 1838 default: 1839 return NOT_HANDLED; 1840 } 1841 return HANDLED; 1842 } 1843 1844 // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this 1845 private void processConnectionEvent(int state, BluetoothDevice device) { 1846 switch (state) { 1847 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1848 if (mCurrentDevice.equals(device)) { 1849 processAudioEvent(HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED, 1850 device); 1851 broadcastConnectionState(mCurrentDevice, 1852 BluetoothProfile.STATE_DISCONNECTED, 1853 BluetoothProfile.STATE_CONNECTED); 1854 transitionTo(mDisconnected); 1855 } else { 1856 Log.e(TAG, "Disconnected from unknown device: " + device); 1857 } 1858 break; 1859 default: 1860 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1861 break; 1862 } 1863 } 1864 1865 // in AudioOn state 1866 private void processAudioEvent(int state, BluetoothDevice device) { 1867 if (!mCurrentDevice.equals(device)) { 1868 Log.e(TAG, "Audio changed on disconnected device: " + device); 1869 return; 1870 } 1871 1872 switch (state) { 1873 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: 1874 if (mAudioState != BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED) { 1875 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1876 // Audio focus may still be held by the entity controlling the actual call 1877 // (such as Telecom) and hence this will still keep the call around, there 1878 // is not much we can do here since dropping the call without user consent 1879 // even if the audio connection snapped may not be a good idea. 1880 if (DBG) { 1881 Log.d(TAG,"hfp_enable=false"); 1882 } 1883 mAudioManager.setParameters("hfp_enable=false"); 1884 broadcastAudioState(device, 1885 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1886 BluetoothHeadsetClient.STATE_AUDIO_CONNECTED); 1887 } 1888 1889 transitionTo(mConnected); 1890 break; 1891 default: 1892 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1893 break; 1894 } 1895 } 1896 1897 @Override 1898 public void exit() { 1899 if (DBG) { 1900 Log.d(TAG, "Exit AudioOn: " + getCurrentMessage().what); 1901 } 1902 } 1903 } 1904 1905 /** 1906 * @hide 1907 */ 1908 public synchronized int getConnectionState(BluetoothDevice device) { 1909 if (mCurrentDevice == null) { 1910 return BluetoothProfile.STATE_DISCONNECTED; 1911 } 1912 1913 if (!mCurrentDevice.equals(device)) { 1914 return BluetoothProfile.STATE_DISCONNECTED; 1915 } 1916 1917 IState currentState = getCurrentState(); 1918 if (currentState == mConnecting) { 1919 return BluetoothProfile.STATE_CONNECTING; 1920 } 1921 1922 if (currentState == mConnected || currentState == mAudioOn) { 1923 return BluetoothProfile.STATE_CONNECTED; 1924 } 1925 1926 Log.e(TAG, "Bad currentState: " + currentState); 1927 return BluetoothProfile.STATE_DISCONNECTED; 1928 } 1929 1930 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 1931 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED); 1932 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1933 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1934 1935 if (newState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) { 1936 intent.putExtra(BluetoothHeadsetClient.EXTRA_AUDIO_WBS, mAudioWbs); 1937 } 1938 1939 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1940 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1941 if (DBG) { 1942 Log.d(TAG, "Audio state " + device + ": " + prevState + "->" + newState); 1943 } 1944 } 1945 1946 // This method does not check for error condition (newState == prevState) 1947 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 1948 if (DBG) { 1949 Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState); 1950 } 1951 /* 1952 * Notifying the connection state change of the profile before sending 1953 * the intent for connection state change, as it was causing a race 1954 * condition, with the UI not being updated with the correct connection 1955 * state. 1956 */ 1957 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET_CLIENT, 1958 newState, prevState); 1959 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); 1960 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1961 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1962 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1963 1964 // add feature extras when connected 1965 if (newState == BluetoothProfile.STATE_CONNECTED) { 1966 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY) == 1967 HeadsetClientHalConstants.PEER_FEAT_3WAY) { 1968 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true); 1969 } 1970 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC) == 1971 HeadsetClientHalConstants.PEER_FEAT_VREC) { 1972 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true); 1973 } 1974 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VTAG) == 1975 HeadsetClientHalConstants.PEER_FEAT_VTAG) { 1976 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT, true); 1977 } 1978 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT) == 1979 HeadsetClientHalConstants.PEER_FEAT_REJECT) { 1980 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true); 1981 } 1982 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC) == 1983 HeadsetClientHalConstants.PEER_FEAT_ECC) { 1984 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true); 1985 } 1986 1987 // add individual CHLD support extras 1988 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) == 1989 HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) { 1990 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true); 1991 } 1992 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL) == 1993 HeadsetClientHalConstants.CHLD_FEAT_REL) { 1994 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true); 1995 } 1996 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) == 1997 HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) { 1998 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true); 1999 } 2000 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE) == 2001 HeadsetClientHalConstants.CHLD_FEAT_MERGE) { 2002 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true); 2003 } 2004 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) == 2005 HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) { 2006 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true); 2007 } 2008 } 2009 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 2010 } 2011 2012 boolean isConnected() { 2013 IState currentState = getCurrentState(); 2014 return (currentState == mConnected || currentState == mAudioOn); 2015 } 2016 2017 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 2018 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 2019 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 2020 int connectionState; 2021 synchronized (this) { 2022 for (BluetoothDevice device : bondedDevices) { 2023 ParcelUuid[] featureUuids = device.getUuids(); 2024 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.Handsfree_AG)) { 2025 continue; 2026 } 2027 connectionState = getConnectionState(device); 2028 for (int state : states) { 2029 if (connectionState == state) { 2030 deviceList.add(device); 2031 } 2032 } 2033 } 2034 } 2035 return deviceList; 2036 } 2037 2038 boolean okToConnect(BluetoothDevice device) { 2039 int priority = mService.getPriority(device); 2040 boolean ret = false; 2041 // check priority and accept or reject the connection. if priority is 2042 // undefined 2043 // it is likely that our SDP has not completed and peer is initiating 2044 // the 2045 // connection. Allow this connection, provided the device is bonded 2046 if ((BluetoothProfile.PRIORITY_OFF < priority) || 2047 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 2048 (device.getBondState() != BluetoothDevice.BOND_NONE))) { 2049 ret = true; 2050 } 2051 return ret; 2052 } 2053 2054 boolean isAudioOn() { 2055 return (getCurrentState() == mAudioOn); 2056 } 2057 2058 public void setAudioRouteAllowed(boolean allowed) { 2059 mAudioRouteAllowed = allowed; 2060 } 2061 2062 public boolean getAudioRouteAllowed() { 2063 return mAudioRouteAllowed; 2064 } 2065 2066 synchronized int getAudioState(BluetoothDevice device) { 2067 if (mCurrentDevice == null || !mCurrentDevice.equals(device)) { 2068 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 2069 } 2070 return mAudioState; 2071 } 2072 2073 /** 2074 * @hide 2075 */ 2076 List<BluetoothDevice> getConnectedDevices() { 2077 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 2078 synchronized (this) { 2079 if (isConnected()) { 2080 devices.add(mCurrentDevice); 2081 } 2082 } 2083 return devices; 2084 } 2085 2086 private BluetoothDevice getDevice(byte[] address) { 2087 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 2088 } 2089 2090 private void onConnectionStateChanged(int state, int peer_feat, int chld_feat, byte[] address) { 2091 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 2092 event.valueInt = state; 2093 event.valueInt2 = peer_feat; 2094 event.valueInt3 = chld_feat; 2095 event.device = getDevice(address); 2096 if (DBG) { 2097 Log.d(TAG, "incoming" + event); 2098 } 2099 sendMessage(STACK_EVENT, event); 2100 } 2101 2102 private void onAudioStateChanged(int state, byte[] address) { 2103 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 2104 event.valueInt = state; 2105 event.device = getDevice(address); 2106 if (DBG) { 2107 Log.d(TAG, "incoming" + event); 2108 } 2109 sendMessage(STACK_EVENT, event); 2110 } 2111 2112 private void onVrStateChanged(int state) { 2113 StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED); 2114 event.valueInt = state; 2115 if (DBG) { 2116 Log.d(TAG, "incoming" + event); 2117 } 2118 sendMessage(STACK_EVENT, event); 2119 } 2120 2121 private void onNetworkState(int state) { 2122 StackEvent event = new StackEvent(EVENT_TYPE_NETWORK_STATE); 2123 event.valueInt = state; 2124 if (DBG) { 2125 Log.d(TAG, "incoming" + event); 2126 } 2127 sendMessage(STACK_EVENT, event); 2128 } 2129 2130 private void onNetworkRoaming(int state) { 2131 StackEvent event = new StackEvent(EVENT_TYPE_ROAMING_STATE); 2132 event.valueInt = state; 2133 if (DBG) { 2134 Log.d(TAG, "incoming" + event); 2135 } 2136 sendMessage(STACK_EVENT, event); 2137 } 2138 2139 private void onNetworkSignal(int signal) { 2140 StackEvent event = new StackEvent(EVENT_TYPE_NETWORK_SIGNAL); 2141 event.valueInt = signal; 2142 if (DBG) { 2143 Log.d(TAG, "incoming" + event); 2144 } 2145 sendMessage(STACK_EVENT, event); 2146 } 2147 2148 private void onBatteryLevel(int level) { 2149 StackEvent event = new StackEvent(EVENT_TYPE_BATTERY_LEVEL); 2150 event.valueInt = level; 2151 if (DBG) { 2152 Log.d(TAG, "incoming" + event); 2153 } 2154 sendMessage(STACK_EVENT, event); 2155 } 2156 2157 private void onCurrentOperator(String name) { 2158 StackEvent event = new StackEvent(EVENT_TYPE_OPERATOR_NAME); 2159 event.valueString = name; 2160 if (DBG) { 2161 Log.d(TAG, "incoming" + event); 2162 } 2163 sendMessage(STACK_EVENT, event); 2164 } 2165 2166 /** 2167 * CIEV (Call indicators) notifying if a call is in progress. 2168 * 2169 * Values Include: 2170 * 0 - No call in progress 2171 * 1 - Atleast 1 call is in progress 2172 */ 2173 private void onCall(int call) { 2174 StackEvent event = new StackEvent(EVENT_TYPE_CALL); 2175 event.valueInt = call; 2176 if (DBG) { 2177 Log.d(TAG, "incoming" + event); 2178 } 2179 sendMessage(STACK_EVENT, event); 2180 } 2181 2182 /** 2183 * CIEV (Call indicators) notifying if call(s) are getting set up. 2184 * 2185 * Values incldue: 2186 * 0 - No current call is in setup 2187 * 1 - Incoming call process ongoing 2188 * 2 - Outgoing call process ongoing 2189 * 3 - Remote party being alerted for outgoing call 2190 */ 2191 private void onCallSetup(int callsetup) { 2192 StackEvent event = new StackEvent(EVENT_TYPE_CALLSETUP); 2193 event.valueInt = callsetup; 2194 if (DBG) { 2195 Log.d(TAG, "incoming" + event); 2196 } 2197 sendMessage(STACK_EVENT, event); 2198 } 2199 2200 /** 2201 * CIEV (Call indicators) notifying call held states. 2202 * 2203 * Values include: 2204 * 0 - No calls held 2205 * 1 - Call is placed on hold or active/held calls wapped (The AG has both an ACTIVE and HELD 2206 * call) 2207 * 2 - Call on hold, no active call 2208 */ 2209 private void onCallHeld(int callheld) { 2210 StackEvent event = new StackEvent(EVENT_TYPE_CALLHELD); 2211 event.valueInt = callheld; 2212 if (DBG) { 2213 Log.d(TAG, "incoming" + event); 2214 } 2215 sendMessage(STACK_EVENT, event); 2216 } 2217 2218 private void onRespAndHold(int resp_and_hold) { 2219 StackEvent event = new StackEvent(EVENT_TYPE_RESP_AND_HOLD); 2220 event.valueInt = resp_and_hold; 2221 if (DBG) { 2222 Log.d(TAG, "incoming" + event); 2223 } 2224 sendMessage(STACK_EVENT, event); 2225 } 2226 2227 private void onClip(String number) { 2228 StackEvent event = new StackEvent(EVENT_TYPE_CLIP); 2229 event.valueString = number; 2230 if (DBG) { 2231 Log.d(TAG, "incoming" + event); 2232 } 2233 sendMessage(STACK_EVENT, event); 2234 } 2235 2236 private void onCallWaiting(String number) { 2237 StackEvent event = new StackEvent(EVENT_TYPE_CALL_WAITING); 2238 event.valueString = number; 2239 if (DBG) { 2240 Log.d(TAG, "incoming" + event); 2241 } 2242 sendMessage(STACK_EVENT, event); 2243 } 2244 2245 private void onCurrentCalls(int index, int dir, int state, int mparty, String number) { 2246 StackEvent event = new StackEvent(EVENT_TYPE_CURRENT_CALLS); 2247 event.valueInt = index; 2248 event.valueInt2 = dir; 2249 event.valueInt3 = state; 2250 event.valueInt4 = mparty; 2251 event.valueString = number; 2252 if (DBG) { 2253 Log.d(TAG, "incoming" + event); 2254 } 2255 sendMessage(STACK_EVENT, event); 2256 } 2257 2258 private void onVolumeChange(int type, int volume) { 2259 StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED); 2260 event.valueInt = type; 2261 event.valueInt2 = volume; 2262 if (DBG) { 2263 Log.d(TAG, "incoming" + event); 2264 } 2265 sendMessage(STACK_EVENT, event); 2266 } 2267 2268 private void onCmdResult(int type, int cme) { 2269 StackEvent event = new StackEvent(EVENT_TYPE_CMD_RESULT); 2270 event.valueInt = type; 2271 event.valueInt2 = cme; 2272 if (DBG) { 2273 Log.d(TAG, "incoming" + event); 2274 } 2275 sendMessage(STACK_EVENT, event); 2276 } 2277 2278 private void onSubscriberInfo(String number, int type) { 2279 StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_INFO); 2280 event.valueInt = type; 2281 event.valueString = number; 2282 if (DBG) { 2283 Log.d(TAG, "incoming" + event); 2284 } 2285 sendMessage(STACK_EVENT, event); 2286 } 2287 2288 private void onInBandRing(int in_band) { 2289 StackEvent event = new StackEvent(EVENT_TYPE_IN_BAND_RING); 2290 event.valueInt = in_band; 2291 if (DBG) { 2292 Log.d(TAG, "incoming" + event); 2293 } 2294 sendMessage(STACK_EVENT, event); 2295 } 2296 2297 private void onLastVoiceTagNumber(String number) { 2298 StackEvent event = new StackEvent(EVENT_TYPE_LAST_VOICE_TAG_NUMBER); 2299 event.valueString = number; 2300 if (DBG) { 2301 Log.d(TAG, "incoming" + event); 2302 } 2303 sendMessage(STACK_EVENT, event); 2304 } 2305 2306 private void onRingIndication() { 2307 StackEvent event = new StackEvent(EVENT_TYPE_RING_INDICATION); 2308 if (DBG) { 2309 Log.d(TAG, "incoming" + event); 2310 } 2311 sendMessage(STACK_EVENT, event); 2312 } 2313 2314 private String getCurrentDeviceName() { 2315 String defaultName = "<unknown>"; 2316 if (mCurrentDevice == null) { 2317 return defaultName; 2318 } 2319 String deviceName = mCurrentDevice.getName(); 2320 if (deviceName == null) { 2321 return defaultName; 2322 } 2323 return deviceName; 2324 } 2325 2326 private byte[] getByteAddress(BluetoothDevice device) { 2327 return Utils.getBytesFromAddress(device.getAddress()); 2328 } 2329 2330 // Event types for STACK_EVENT message 2331 final private static int EVENT_TYPE_NONE = 0; 2332 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 2333 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 2334 final private static int EVENT_TYPE_VR_STATE_CHANGED = 3; 2335 final private static int EVENT_TYPE_NETWORK_STATE = 4; 2336 final private static int EVENT_TYPE_ROAMING_STATE = 5; 2337 final private static int EVENT_TYPE_NETWORK_SIGNAL = 6; 2338 final private static int EVENT_TYPE_BATTERY_LEVEL = 7; 2339 final private static int EVENT_TYPE_OPERATOR_NAME = 8; 2340 final private static int EVENT_TYPE_CALL = 9; 2341 final private static int EVENT_TYPE_CALLSETUP = 10; 2342 final private static int EVENT_TYPE_CALLHELD = 11; 2343 final private static int EVENT_TYPE_CLIP = 12; 2344 final private static int EVENT_TYPE_CALL_WAITING = 13; 2345 final private static int EVENT_TYPE_CURRENT_CALLS = 14; 2346 final private static int EVENT_TYPE_VOLUME_CHANGED = 15; 2347 final private static int EVENT_TYPE_CMD_RESULT = 16; 2348 final private static int EVENT_TYPE_SUBSCRIBER_INFO = 17; 2349 final private static int EVENT_TYPE_RESP_AND_HOLD = 18; 2350 final private static int EVENT_TYPE_IN_BAND_RING = 19; 2351 final private static int EVENT_TYPE_LAST_VOICE_TAG_NUMBER = 20; 2352 final private static int EVENT_TYPE_RING_INDICATION= 21; 2353 2354 // for debugging only 2355 private final String EVENT_TYPE_NAMES[] = 2356 { 2357 "EVENT_TYPE_NONE", 2358 "EVENT_TYPE_CONNECTION_STATE_CHANGED", 2359 "EVENT_TYPE_AUDIO_STATE_CHANGED", 2360 "EVENT_TYPE_VR_STATE_CHANGED", 2361 "EVENT_TYPE_NETWORK_STATE", 2362 "EVENT_TYPE_ROAMING_STATE", 2363 "EVENT_TYPE_NETWORK_SIGNAL", 2364 "EVENT_TYPE_BATTERY_LEVEL", 2365 "EVENT_TYPE_OPERATOR_NAME", 2366 "EVENT_TYPE_CALL", 2367 "EVENT_TYPE_CALLSETUP", 2368 "EVENT_TYPE_CALLHELD", 2369 "EVENT_TYPE_CLIP", 2370 "EVENT_TYPE_CALL_WAITING", 2371 "EVENT_TYPE_CURRENT_CALLS", 2372 "EVENT_TYPE_VOLUME_CHANGED", 2373 "EVENT_TYPE_CMD_RESULT", 2374 "EVENT_TYPE_SUBSCRIBER_INFO", 2375 "EVENT_TYPE_RESP_AND_HOLD", 2376 "EVENT_TYPE_IN_BAND_RING", 2377 "EVENT_TYPE_LAST_VOICE_TAG_NUMBER", 2378 "EVENT_TYPE_RING_INDICATION", 2379 }; 2380 2381 private class StackEvent { 2382 int type = EVENT_TYPE_NONE; 2383 int valueInt = 0; 2384 int valueInt2 = 0; 2385 int valueInt3 = 0; 2386 int valueInt4 = 0; 2387 String valueString = null; 2388 BluetoothDevice device = null; 2389 2390 private StackEvent(int type) { 2391 this.type = type; 2392 } 2393 2394 @Override 2395 public String toString() { 2396 // event dump 2397 StringBuilder result = new StringBuilder(); 2398 result.append("StackEvent {type:" + EVENT_TYPE_NAMES[type]); 2399 result.append(", value1:" + valueInt); 2400 result.append(", value2:" + valueInt2); 2401 result.append(", value3:" + valueInt3); 2402 result.append(", value4:" + valueInt4); 2403 result.append(", string: \"" + valueString + "\""); 2404 result.append(", device:" + device + "}"); 2405 return result.toString(); 2406 } 2407 } 2408 2409 private native static void classInitNative(); 2410 2411 private native void initializeNative(); 2412 2413 private native void cleanupNative(); 2414 2415 private native boolean connectNative(byte[] address); 2416 2417 private native boolean disconnectNative(byte[] address); 2418 2419 private native boolean connectAudioNative(byte[] address); 2420 2421 private native boolean disconnectAudioNative(byte[] address); 2422 2423 private native boolean startVoiceRecognitionNative(); 2424 2425 private native boolean stopVoiceRecognitionNative(); 2426 2427 private native boolean setVolumeNative(int volumeType, int volume); 2428 2429 private native boolean dialNative(String number); 2430 2431 private native boolean dialMemoryNative(int location); 2432 2433 private native boolean handleCallActionNative(int action, int index); 2434 2435 private native boolean queryCurrentCallsNative(); 2436 2437 private native boolean queryCurrentOperatorNameNative(); 2438 2439 private native boolean retrieveSubscriberInfoNative(); 2440 2441 private native boolean sendDtmfNative(byte code); 2442 2443 private native boolean requestLastVoiceTagNumberNative(); 2444 2445 private native boolean sendATCmdNative(int ATCmd, int val1, 2446 int val2, String arg); 2447 2448 public List<BluetoothHeadsetClientCall> getCurrentCalls() { 2449 return new ArrayList<BluetoothHeadsetClientCall>(mCalls.values()); 2450 } 2451 2452 public Bundle getCurrentAgEvents() { 2453 Bundle b = new Bundle(); 2454 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, mIndicatorNetworkState); 2455 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, mIndicatorNetworkSignal); 2456 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, mIndicatorNetworkType); 2457 b.putInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, mIndicatorBatteryLevel); 2458 b.putString(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName); 2459 b.putInt(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, mVoiceRecognitionActive); 2460 b.putInt(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, mInBandRingtone); 2461 b.putString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo); 2462 return b; 2463 } 2464} 2465