Loading src/com/android/bluetooth/btservice/AdapterService.java +23 −2 Original line number Original line Diff line number Diff line Loading @@ -1800,6 +1800,11 @@ public class AdapterService extends Service { return mAdapterProperties.discoveryEndMillis(); return mAdapterProperties.discoveryEndMillis(); } } /** * Same as API method {@link BluetoothAdapter#getBondedDevices()} * * @return array of bonded {@link BluetoothDevice} or null on error */ public BluetoothDevice[] getBondedDevices() { public BluetoothDevice[] getBondedDevices() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getBondedDevices(); return mAdapterProperties.getBondedDevices(); Loading Loading @@ -1895,7 +1900,17 @@ public class AdapterService extends Service { return true; return true; } } int getBondState(BluetoothDevice device) { /** * Get the bond state of a particular {@link BluetoothDevice} * * @param device remote device of interest * @return bond state <p>Possible values are * {@link BluetoothDevice#BOND_NONE}, * {@link BluetoothDevice#BOND_BONDING}, * {@link BluetoothDevice#BOND_BONDED}. */ @VisibleForTesting public int getBondState(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) { if (deviceProp == null) { Loading Loading @@ -1923,7 +1938,13 @@ public class AdapterService extends Service { return getConnectionStateNative(addr); return getConnectionStateNative(addr); } } String getRemoteName(BluetoothDevice device) { /** * Same as API method {@link BluetoothDevice#getName()} * * @param device remote device of interest * @return remote device name */ public String getRemoteName(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (mRemoteDevices == null) { if (mRemoteDevices == null) { return null; return null; Loading src/com/android/bluetooth/hfp/HeadsetObjectsFactory.java +7 −4 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.os.Looper; import android.util.Log; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; /** /** * Factory class for object initialization to help with unit testing * Factory class for object initialization to help with unit testing Loading Loading @@ -63,15 +64,17 @@ public class HeadsetObjectsFactory { * * * @param device the remote device associated with this state machine * @param device the remote device associated with this state machine * @param looper the thread that the state machine is supposed to run on * @param looper the thread that the state machine is supposed to run on * @param service the headset service * @param headsetService the headset service * @param adapterService the adapter service * @param nativeInterface native interface * @param nativeInterface native interface * @param systemInterface system interface * @param systemInterface system interface * @return a state machine that is initialized and started, ready to go * @return a state machine that is initialized and started, ready to go */ */ public HeadsetStateMachine makeStateMachine(BluetoothDevice device, Looper looper, public HeadsetStateMachine makeStateMachine(BluetoothDevice device, Looper looper, HeadsetService service, HeadsetNativeInterface nativeInterface, HeadsetService headsetService, AdapterService adapterService, HeadsetSystemInterface systemInterface) { HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { return HeadsetStateMachine.make(device, looper, service, nativeInterface, systemInterface); return HeadsetStateMachine.make(device, looper, headsetService, adapterService, nativeInterface, systemInterface); } } /** /** Loading src/com/android/bluetooth/hfp/HeadsetService.java +48 −16 Original line number Original line Diff line number Diff line Loading @@ -234,10 +234,8 @@ public class HeadsetService extends ProfileService { * @param stackEvent event from native stack * @param stackEvent event from native stack */ */ void messageFromNative(HeadsetStackEvent stackEvent) { void messageFromNative(HeadsetStackEvent stackEvent) { if (stackEvent.device == null) { Objects.requireNonNull(stackEvent.device, Log.wtfStack(TAG, "messageFromNative, device is null, event: " + stackEvent); "Device should never be null, event: " + stackEvent); return; } synchronized (mStateMachines) { synchronized (mStateMachines) { HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device); HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device); if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { Loading @@ -248,16 +246,17 @@ public class HeadsetService extends ProfileService { if (stateMachine == null) { if (stateMachine == null) { stateMachine = HeadsetObjectsFactory.getInstance() stateMachine = HeadsetObjectsFactory.getInstance() .makeStateMachine(stackEvent.device, .makeStateMachine(stackEvent.device, mStateMachinesThread.getLooper(), this, mStateMachinesThread.getLooper(), this, mAdapterService, mNativeInterface, mSystemInterface); mNativeInterface, mSystemInterface); mStateMachines.put(stackEvent.device, stateMachine); mStateMachines.put(stackEvent.device, stateMachine); } } break; break; } } } } } else if (stateMachine == null) { } Log.wtfStack(TAG, "State machine not found for stack event: " + stackEvent); if (stateMachine == null) { return; throw new IllegalStateException( "State machine not found for stack event: " + stackEvent); } } stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent); stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent); } } Loading Loading @@ -315,8 +314,9 @@ public class HeadsetService extends ProfileService { case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); BluetoothDevice.ERROR); BluetoothDevice device = BluetoothDevice device = Objects.requireNonNull( intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); logD("Bond state changed for device: " + device + " state: " + state); logD("Bond state changed for device: " + device + " state: " + state); if (state != BluetoothDevice.BOND_NONE) { if (state != BluetoothDevice.BOND_NONE) { break; break; Loading @@ -326,9 +326,11 @@ public class HeadsetService extends ProfileService { if (stateMachine == null) { if (stateMachine == null) { break; break; } } logD("Removing state machine for device: " + device); if (stateMachine.getConnectionState() HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); != BluetoothProfile.STATE_DISCONNECTED) { mStateMachines.remove(device); break; } removeStateMachine(device); } } break; break; } } Loading Loading @@ -626,13 +628,18 @@ public class HeadsetService extends ProfileService { Log.w(TAG, "connect: PRIORITY_OFF, device=" + device); Log.w(TAG, "connect: PRIORITY_OFF, device=" + device); return false; return false; } } ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID"); return false; } synchronized (mStateMachines) { synchronized (mStateMachines) { Log.i(TAG, "connect: device=" + device); Log.i(TAG, "connect: device=" + device); HeadsetStateMachine stateMachine = mStateMachines.get(device); HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { if (stateMachine == null) { stateMachine = HeadsetObjectsFactory.getInstance() stateMachine = HeadsetObjectsFactory.getInstance() .makeStateMachine(device, mStateMachinesThread.getLooper(), this, .makeStateMachine(device, mStateMachinesThread.getLooper(), this, mNativeInterface, mSystemInterface); mAdapterService, mNativeInterface, mSystemInterface); mStateMachines.put(device, stateMachine); mStateMachines.put(device, stateMachine); } } int connectionState = stateMachine.getConnectionState(); int connectionState = stateMachine.getConnectionState(); Loading Loading @@ -699,7 +706,14 @@ public class HeadsetService extends ProfileService { return devices; return devices; } } private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { /** * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])} * * @param states an array of states from {@link BluetoothProfile} * @return a list of devices matching the array of connection states */ @VisibleForTesting public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); ArrayList<BluetoothDevice> devices = new ArrayList<>(); ArrayList<BluetoothDevice> devices = new ArrayList<>(); if (states == null) { if (states == null) { Loading Loading @@ -1217,7 +1231,7 @@ public class HeadsetService extends ProfileService { // Check priority and accept or reject the connection. // Check priority and accept or reject the connection. // Note: Logic can be simplified, but keeping it this way for readability // Note: Logic can be simplified, but keeping it this way for readability int priority = getPriority(device); int priority = getPriority(device); int bondState = device.getBondState(); int bondState = mAdapterService.getBondState(device); // If priority is undefined, it is likely that our SDP has not completed and peer is // If priority is undefined, it is likely that our SDP has not completed and peer is // initiating the connection. Allow this connection only if the device is bonded or bonding // initiating the connection. Allow this connection only if the device is bonded or bonding if ((priority == BluetoothProfile.PRIORITY_UNDEFINED) && (bondState if ((priority == BluetoothProfile.PRIORITY_UNDEFINED) && (bondState Loading @@ -1240,6 +1254,24 @@ public class HeadsetService extends ProfileService { return true; return true; } } /** * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice} * * @param device device whose state machine is to be removed. */ void removeStateMachine(BluetoothDevice device) { synchronized (mStateMachines) { HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine"); return; } Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device); HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); mStateMachines.remove(device); } } @Override @Override public void dump(StringBuilder sb) { public void dump(StringBuilder sb) { synchronized (mStateMachines) { synchronized (mStateMachines) { Loading src/com/android/bluetooth/hfp/HeadsetStateMachine.java +45 −32 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.support.annotation.VisibleForTesting; import android.telephony.PhoneNumberUtils; import android.telephony.PhoneNumberUtils; import android.util.Log; import android.util.Log; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ProfileService; import com.android.internal.util.State; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.StateMachine; Loading Loading @@ -129,7 +130,8 @@ public class HeadsetStateMachine extends StateMachine { private HeadsetStateBase mPrevState; private HeadsetStateBase mPrevState; // Run time dependencies // Run time dependencies private final HeadsetService mService; private final HeadsetService mHeadsetService; private final AdapterService mAdapterService; private final HeadsetNativeInterface mNativeInterface; private final HeadsetNativeInterface mNativeInterface; private final HeadsetSystemInterface mSystemInterface; private final HeadsetSystemInterface mSystemInterface; Loading Loading @@ -170,19 +172,21 @@ public class HeadsetStateMachine extends StateMachine { VOICE_COMMAND_INTENT.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); VOICE_COMMAND_INTENT.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } } private HeadsetStateMachine(BluetoothDevice device, Looper looper, HeadsetService service, private HeadsetStateMachine(BluetoothDevice device, Looper looper, HeadsetService headsetService, AdapterService adapterService, HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { super(TAG, Objects.requireNonNull(looper, "looper cannot be null")); super(TAG, Objects.requireNonNull(looper, "looper cannot be null")); // Enable/Disable StateMachine debug logs // Enable/Disable StateMachine debug logs setDbg(DBG); setDbg(DBG); mDevice = Objects.requireNonNull(device, "device cannot be null"); mDevice = Objects.requireNonNull(device, "device cannot be null"); mService = Objects.requireNonNull(service, "service cannot be null"); mHeadsetService = Objects.requireNonNull(headsetService, "headsetService cannot be null"); mNativeInterface = mNativeInterface = Objects.requireNonNull(nativeInterface, "nativeInterface cannot be null"); Objects.requireNonNull(nativeInterface, "nativeInterface cannot be null"); mSystemInterface = mSystemInterface = Objects.requireNonNull(systemInterface, "systemInterface cannot be null"); Objects.requireNonNull(systemInterface, "systemInterface cannot be null"); mAdapterService = Objects.requireNonNull(adapterService, "AdapterService cannot be null"); // Create phonebook helper // Create phonebook helper mPhonebook = new AtPhonebook(mService, mNativeInterface); mPhonebook = new AtPhonebook(mHeadsetService, mNativeInterface); // Initialize state machine // Initialize state machine addState(mDisconnected); addState(mDisconnected); addState(mConnecting); addState(mConnecting); Loading @@ -194,12 +198,14 @@ public class HeadsetStateMachine extends StateMachine { setInitialState(mDisconnected); setInitialState(mDisconnected); } } static HeadsetStateMachine make(BluetoothDevice device, Looper looper, HeadsetService service, static HeadsetStateMachine make(BluetoothDevice device, Looper looper, HeadsetService headsetService, AdapterService adapterService, HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { Log.i(TAG, "make"); HeadsetStateMachine stateMachine = HeadsetStateMachine stateMachine = new HeadsetStateMachine(device, looper, service, nativeInterface, systemInterface); new HeadsetStateMachine(device, looper, headsetService, adapterService, nativeInterface, systemInterface); stateMachine.start(); stateMachine.start(); Log.i(TAG, "Created state machine " + stateMachine + " for " + device); return stateMachine; return stateMachine; } } Loading Loading @@ -302,13 +308,14 @@ public class HeadsetStateMachine extends StateMachine { // Headset is disconnecting, stop Virtual call if active. // Headset is disconnecting, stop Virtual call if active. terminateScoUsingVirtualVoiceCall(); terminateScoUsingVirtualVoiceCall(); } } mService.onConnectionStateChangedFromStateMachine(device, fromState, toState); mHeadsetService.onConnectionStateChangedFromStateMachine(device, fromState, toState); Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); } } // Should not be called from enter() method // Should not be called from enter() method Loading @@ -319,12 +326,13 @@ public class HeadsetStateMachine extends StateMachine { // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. terminateScoUsingVirtualVoiceCall(); terminateScoUsingVirtualVoiceCall(); } } mService.onAudioStateChangedFromStateMachine(device, fromState, toState); mHeadsetService.onAudioStateChangedFromStateMachine(device, fromState, toState); Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); } } /** /** Loading Loading @@ -451,6 +459,11 @@ public class HeadsetStateMachine extends StateMachine { mWaitingForVoiceRecognition = false; mWaitingForVoiceRecognition = false; mAudioParams.clear(); mAudioParams.clear(); broadcastStateTransitions(); broadcastStateTransitions(); // Remove the state machine for unbonded devices if (mPrevState != null && mAdapterService.getBondState(mDevice) == BluetoothDevice.BOND_NONE) { getHandler().post(() -> mHeadsetService.removeStateMachine(mDevice)); } } } @Override @Override Loading Loading @@ -516,12 +529,12 @@ public class HeadsetStateMachine extends StateMachine { // Both events result in Connecting state as SLC establishment is still required // Both events result in Connecting state as SLC establishment is still required case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: if (mService.okToAcceptConnection(mDevice)) { if (mHeadsetService.okToAcceptConnection(mDevice)) { stateLogI("accept incoming connection"); stateLogI("accept incoming connection"); transitionTo(mConnecting); transitionTo(mConnecting); } else { } else { stateLogI("rejected incoming HF, priority=" + mService.getPriority(mDevice) stateLogI("rejected incoming HF, priority=" + mHeadsetService.getPriority( + " bondState=" + mDevice.getBondState()); mDevice) + " bondState=" + mAdapterService.getBondState(mDevice)); // Reject the connection and stay in Disconnected state itself // Reject the connection and stay in Disconnected state itself if (!mNativeInterface.disconnectHfp(mDevice)) { if (!mNativeInterface.disconnectHfp(mDevice)) { stateLogE("failed to disconnect"); stateLogE("failed to disconnect"); Loading Loading @@ -1219,8 +1232,8 @@ public class HeadsetStateMachine extends StateMachine { // Set active device to current active SCO device when the current active device // Set active device to current active SCO device when the current active device // is different from mCurrentDevice. This is to accommodate active device state // is different from mCurrentDevice. This is to accommodate active device state // mis-match between native and Java. // mis-match between native and Java. if (!mDevice.equals(mService.getActiveDevice())) { if (!mDevice.equals(mHeadsetService.getActiveDevice())) { mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); } } setAudioParameters(); setAudioParameters(); mSystemInterface.getAudioManager().setBluetoothScoOn(true); mSystemInterface.getAudioManager().setBluetoothScoOn(true); Loading Loading @@ -1460,7 +1473,7 @@ public class HeadsetStateMachine extends StateMachine { } } } } try { try { mService.startActivity(VOICE_COMMAND_INTENT); mHeadsetService.startActivity(VOICE_COMMAND_INTENT); } catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) { mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 0); Loading Loading @@ -1549,7 +1562,7 @@ public class HeadsetStateMachine extends StateMachine { private synchronized void expectVoiceRecognition(BluetoothDevice device) { private synchronized void expectVoiceRecognition(BluetoothDevice device) { mWaitingForVoiceRecognition = true; mWaitingForVoiceRecognition = true; mService.setActiveDevice(device); mHeadsetService.setActiveDevice(device); sendMessageDelayed(START_VR_TIMEOUT, device, START_VR_TIMEOUT_MS); sendMessageDelayed(START_VR_TIMEOUT, device, START_VR_TIMEOUT_MS); if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { mSystemInterface.getVoiceRecognitionWakeLock().acquire(START_VR_TIMEOUT_MS); mSystemInterface.getVoiceRecognitionWakeLock().acquire(START_VR_TIMEOUT_MS); Loading @@ -1576,7 +1589,7 @@ public class HeadsetStateMachine extends StateMachine { intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + Integer.toString(companyId)); + Integer.toString(companyId)); mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); } } private void setAudioParameters() { private void setAudioParameters() { Loading Loading @@ -1720,11 +1733,11 @@ public class HeadsetStateMachine extends StateMachine { } } // Check for virtual call to terminate before sending Call Intent // Check for virtual call to terminate before sending Call Intent terminateScoUsingVirtualVoiceCall(); terminateScoUsingVirtualVoiceCall(); mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); Uri.fromParts(SCHEME_TEL, dialNumber, null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mService.startActivity(intent); mHeadsetService.startActivity(intent); // TODO(BT) continue send OK reults code after call starts // TODO(BT) continue send OK reults code after call starts // hold wait lock, start a timer, set wait call flag // hold wait lock, start a timer, set wait call flag // Get call started indication from bluetooth phone // Get call started indication from bluetooth phone Loading Loading @@ -1761,7 +1774,7 @@ public class HeadsetStateMachine extends StateMachine { if (!hasMessages(DIALING_OUT_TIMEOUT)) { if (!hasMessages(DIALING_OUT_TIMEOUT)) { return; return; } } mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0); mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0); removeMessages(DIALING_OUT_TIMEOUT); removeMessages(DIALING_OUT_TIMEOUT); } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE Loading Loading @@ -2063,7 +2076,7 @@ public class HeadsetStateMachine extends StateMachine { mSystemInterface.answerCall(device); mSystemInterface.answerCall(device); } else if (phoneState.getNumActiveCall() > 0) { } else if (phoneState.getNumActiveCall() > 0) { if (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { if (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); mNativeInterface.connectAudio(mDevice); mNativeInterface.connectAudio(mDevice); } else { } else { mSystemInterface.hangupCall(device, false); mSystemInterface.hangupCall(device, false); Loading @@ -2074,11 +2087,11 @@ public class HeadsetStateMachine extends StateMachine { log("processKeyPressed, last dial number null"); log("processKeyPressed, last dial number null"); return; return; } } mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); Uri.fromParts(SCHEME_TEL, dialNumber, null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mService.startActivity(intent); mHeadsetService.startActivity(intent); } } } } Loading @@ -2095,7 +2108,7 @@ public class HeadsetStateMachine extends StateMachine { intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId); intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId); intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue); intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue); mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); mHeadsetService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); } } private void processAtBind(String atString, BluetoothDevice device) { private void processAtBind(String atString, BluetoothDevice device) { Loading Loading @@ -2159,7 +2172,7 @@ public class HeadsetStateMachine extends StateMachine { } } private String getCurrentDeviceName() { private String getCurrentDeviceName() { String deviceName = mDevice.getName(); String deviceName = mAdapterService.getRemoteName(mDevice); if (deviceName == null) { if (deviceName == null) { return "<unknown>"; return "<unknown>"; } } Loading @@ -2169,28 +2182,28 @@ public class HeadsetStateMachine extends StateMachine { // Accept incoming SCO only when there is in-band ringing, incoming call, // Accept incoming SCO only when there is in-band ringing, incoming call, // active call, VR activated, active VOIP call // active call, VR activated, active VOIP call private boolean isScoAcceptable() { private boolean isScoAcceptable() { if (mService.getForceScoAudio()) { if (mHeadsetService.getForceScoAudio()) { return true; return true; } } BluetoothDevice activeDevice = mService.getActiveDevice(); BluetoothDevice activeDevice = mHeadsetService.getActiveDevice(); if (!mDevice.equals(activeDevice)) { if (!mDevice.equals(activeDevice)) { Log.w(TAG, "isScoAcceptable: rejected SCO since " + mDevice Log.w(TAG, "isScoAcceptable: rejected SCO since " + mDevice + " is not the current active device " + activeDevice); + " is not the current active device " + activeDevice); return false; return false; } } if (!mService.getAudioRouteAllowed()) { if (!mHeadsetService.getAudioRouteAllowed()) { Log.w(TAG, "isScoAcceptabl: rejected SCO since audio route is not allowed"); Log.w(TAG, "isScoAcceptabl: rejected SCO since audio route is not allowed"); return false; return false; } } if (mSystemInterface.isInCall() || mVoiceRecognitionStarted) { if (mSystemInterface.isInCall() || mVoiceRecognitionStarted) { return true; return true; } } if (mSystemInterface.isRinging() && mService.isInbandRingingEnabled()) { if (mSystemInterface.isRinging() && mHeadsetService.isInbandRingingEnabled()) { return true; return true; } } Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall() Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall() + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing=" + mSystemInterface + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing=" + mSystemInterface .isRinging() + ", inbandRinging=" + mService.isInbandRingingEnabled()); .isRinging() + ", inbandRinging=" + mHeadsetService.isInbandRingingEnabled()); return false; return false; } } Loading tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java 0 → 100644 +341 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
src/com/android/bluetooth/btservice/AdapterService.java +23 −2 Original line number Original line Diff line number Diff line Loading @@ -1800,6 +1800,11 @@ public class AdapterService extends Service { return mAdapterProperties.discoveryEndMillis(); return mAdapterProperties.discoveryEndMillis(); } } /** * Same as API method {@link BluetoothAdapter#getBondedDevices()} * * @return array of bonded {@link BluetoothDevice} or null on error */ public BluetoothDevice[] getBondedDevices() { public BluetoothDevice[] getBondedDevices() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getBondedDevices(); return mAdapterProperties.getBondedDevices(); Loading Loading @@ -1895,7 +1900,17 @@ public class AdapterService extends Service { return true; return true; } } int getBondState(BluetoothDevice device) { /** * Get the bond state of a particular {@link BluetoothDevice} * * @param device remote device of interest * @return bond state <p>Possible values are * {@link BluetoothDevice#BOND_NONE}, * {@link BluetoothDevice#BOND_BONDING}, * {@link BluetoothDevice#BOND_BONDED}. */ @VisibleForTesting public int getBondState(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) { if (deviceProp == null) { Loading Loading @@ -1923,7 +1938,13 @@ public class AdapterService extends Service { return getConnectionStateNative(addr); return getConnectionStateNative(addr); } } String getRemoteName(BluetoothDevice device) { /** * Same as API method {@link BluetoothDevice#getName()} * * @param device remote device of interest * @return remote device name */ public String getRemoteName(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (mRemoteDevices == null) { if (mRemoteDevices == null) { return null; return null; Loading
src/com/android/bluetooth/hfp/HeadsetObjectsFactory.java +7 −4 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.os.Looper; import android.util.Log; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; /** /** * Factory class for object initialization to help with unit testing * Factory class for object initialization to help with unit testing Loading Loading @@ -63,15 +64,17 @@ public class HeadsetObjectsFactory { * * * @param device the remote device associated with this state machine * @param device the remote device associated with this state machine * @param looper the thread that the state machine is supposed to run on * @param looper the thread that the state machine is supposed to run on * @param service the headset service * @param headsetService the headset service * @param adapterService the adapter service * @param nativeInterface native interface * @param nativeInterface native interface * @param systemInterface system interface * @param systemInterface system interface * @return a state machine that is initialized and started, ready to go * @return a state machine that is initialized and started, ready to go */ */ public HeadsetStateMachine makeStateMachine(BluetoothDevice device, Looper looper, public HeadsetStateMachine makeStateMachine(BluetoothDevice device, Looper looper, HeadsetService service, HeadsetNativeInterface nativeInterface, HeadsetService headsetService, AdapterService adapterService, HeadsetSystemInterface systemInterface) { HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { return HeadsetStateMachine.make(device, looper, service, nativeInterface, systemInterface); return HeadsetStateMachine.make(device, looper, headsetService, adapterService, nativeInterface, systemInterface); } } /** /** Loading
src/com/android/bluetooth/hfp/HeadsetService.java +48 −16 Original line number Original line Diff line number Diff line Loading @@ -234,10 +234,8 @@ public class HeadsetService extends ProfileService { * @param stackEvent event from native stack * @param stackEvent event from native stack */ */ void messageFromNative(HeadsetStackEvent stackEvent) { void messageFromNative(HeadsetStackEvent stackEvent) { if (stackEvent.device == null) { Objects.requireNonNull(stackEvent.device, Log.wtfStack(TAG, "messageFromNative, device is null, event: " + stackEvent); "Device should never be null, event: " + stackEvent); return; } synchronized (mStateMachines) { synchronized (mStateMachines) { HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device); HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device); if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { Loading @@ -248,16 +246,17 @@ public class HeadsetService extends ProfileService { if (stateMachine == null) { if (stateMachine == null) { stateMachine = HeadsetObjectsFactory.getInstance() stateMachine = HeadsetObjectsFactory.getInstance() .makeStateMachine(stackEvent.device, .makeStateMachine(stackEvent.device, mStateMachinesThread.getLooper(), this, mStateMachinesThread.getLooper(), this, mAdapterService, mNativeInterface, mSystemInterface); mNativeInterface, mSystemInterface); mStateMachines.put(stackEvent.device, stateMachine); mStateMachines.put(stackEvent.device, stateMachine); } } break; break; } } } } } else if (stateMachine == null) { } Log.wtfStack(TAG, "State machine not found for stack event: " + stackEvent); if (stateMachine == null) { return; throw new IllegalStateException( "State machine not found for stack event: " + stackEvent); } } stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent); stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent); } } Loading Loading @@ -315,8 +314,9 @@ public class HeadsetService extends ProfileService { case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); BluetoothDevice.ERROR); BluetoothDevice device = BluetoothDevice device = Objects.requireNonNull( intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); logD("Bond state changed for device: " + device + " state: " + state); logD("Bond state changed for device: " + device + " state: " + state); if (state != BluetoothDevice.BOND_NONE) { if (state != BluetoothDevice.BOND_NONE) { break; break; Loading @@ -326,9 +326,11 @@ public class HeadsetService extends ProfileService { if (stateMachine == null) { if (stateMachine == null) { break; break; } } logD("Removing state machine for device: " + device); if (stateMachine.getConnectionState() HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); != BluetoothProfile.STATE_DISCONNECTED) { mStateMachines.remove(device); break; } removeStateMachine(device); } } break; break; } } Loading Loading @@ -626,13 +628,18 @@ public class HeadsetService extends ProfileService { Log.w(TAG, "connect: PRIORITY_OFF, device=" + device); Log.w(TAG, "connect: PRIORITY_OFF, device=" + device); return false; return false; } } ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID"); return false; } synchronized (mStateMachines) { synchronized (mStateMachines) { Log.i(TAG, "connect: device=" + device); Log.i(TAG, "connect: device=" + device); HeadsetStateMachine stateMachine = mStateMachines.get(device); HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { if (stateMachine == null) { stateMachine = HeadsetObjectsFactory.getInstance() stateMachine = HeadsetObjectsFactory.getInstance() .makeStateMachine(device, mStateMachinesThread.getLooper(), this, .makeStateMachine(device, mStateMachinesThread.getLooper(), this, mNativeInterface, mSystemInterface); mAdapterService, mNativeInterface, mSystemInterface); mStateMachines.put(device, stateMachine); mStateMachines.put(device, stateMachine); } } int connectionState = stateMachine.getConnectionState(); int connectionState = stateMachine.getConnectionState(); Loading Loading @@ -699,7 +706,14 @@ public class HeadsetService extends ProfileService { return devices; return devices; } } private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { /** * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])} * * @param states an array of states from {@link BluetoothProfile} * @return a list of devices matching the array of connection states */ @VisibleForTesting public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); ArrayList<BluetoothDevice> devices = new ArrayList<>(); ArrayList<BluetoothDevice> devices = new ArrayList<>(); if (states == null) { if (states == null) { Loading Loading @@ -1217,7 +1231,7 @@ public class HeadsetService extends ProfileService { // Check priority and accept or reject the connection. // Check priority and accept or reject the connection. // Note: Logic can be simplified, but keeping it this way for readability // Note: Logic can be simplified, but keeping it this way for readability int priority = getPriority(device); int priority = getPriority(device); int bondState = device.getBondState(); int bondState = mAdapterService.getBondState(device); // If priority is undefined, it is likely that our SDP has not completed and peer is // If priority is undefined, it is likely that our SDP has not completed and peer is // initiating the connection. Allow this connection only if the device is bonded or bonding // initiating the connection. Allow this connection only if the device is bonded or bonding if ((priority == BluetoothProfile.PRIORITY_UNDEFINED) && (bondState if ((priority == BluetoothProfile.PRIORITY_UNDEFINED) && (bondState Loading @@ -1240,6 +1254,24 @@ public class HeadsetService extends ProfileService { return true; return true; } } /** * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice} * * @param device device whose state machine is to be removed. */ void removeStateMachine(BluetoothDevice device) { synchronized (mStateMachines) { HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine"); return; } Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device); HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); mStateMachines.remove(device); } } @Override @Override public void dump(StringBuilder sb) { public void dump(StringBuilder sb) { synchronized (mStateMachines) { synchronized (mStateMachines) { Loading
src/com/android/bluetooth/hfp/HeadsetStateMachine.java +45 −32 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.support.annotation.VisibleForTesting; import android.telephony.PhoneNumberUtils; import android.telephony.PhoneNumberUtils; import android.util.Log; import android.util.Log; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ProfileService; import com.android.internal.util.State; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.StateMachine; Loading Loading @@ -129,7 +130,8 @@ public class HeadsetStateMachine extends StateMachine { private HeadsetStateBase mPrevState; private HeadsetStateBase mPrevState; // Run time dependencies // Run time dependencies private final HeadsetService mService; private final HeadsetService mHeadsetService; private final AdapterService mAdapterService; private final HeadsetNativeInterface mNativeInterface; private final HeadsetNativeInterface mNativeInterface; private final HeadsetSystemInterface mSystemInterface; private final HeadsetSystemInterface mSystemInterface; Loading Loading @@ -170,19 +172,21 @@ public class HeadsetStateMachine extends StateMachine { VOICE_COMMAND_INTENT.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); VOICE_COMMAND_INTENT.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } } private HeadsetStateMachine(BluetoothDevice device, Looper looper, HeadsetService service, private HeadsetStateMachine(BluetoothDevice device, Looper looper, HeadsetService headsetService, AdapterService adapterService, HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { super(TAG, Objects.requireNonNull(looper, "looper cannot be null")); super(TAG, Objects.requireNonNull(looper, "looper cannot be null")); // Enable/Disable StateMachine debug logs // Enable/Disable StateMachine debug logs setDbg(DBG); setDbg(DBG); mDevice = Objects.requireNonNull(device, "device cannot be null"); mDevice = Objects.requireNonNull(device, "device cannot be null"); mService = Objects.requireNonNull(service, "service cannot be null"); mHeadsetService = Objects.requireNonNull(headsetService, "headsetService cannot be null"); mNativeInterface = mNativeInterface = Objects.requireNonNull(nativeInterface, "nativeInterface cannot be null"); Objects.requireNonNull(nativeInterface, "nativeInterface cannot be null"); mSystemInterface = mSystemInterface = Objects.requireNonNull(systemInterface, "systemInterface cannot be null"); Objects.requireNonNull(systemInterface, "systemInterface cannot be null"); mAdapterService = Objects.requireNonNull(adapterService, "AdapterService cannot be null"); // Create phonebook helper // Create phonebook helper mPhonebook = new AtPhonebook(mService, mNativeInterface); mPhonebook = new AtPhonebook(mHeadsetService, mNativeInterface); // Initialize state machine // Initialize state machine addState(mDisconnected); addState(mDisconnected); addState(mConnecting); addState(mConnecting); Loading @@ -194,12 +198,14 @@ public class HeadsetStateMachine extends StateMachine { setInitialState(mDisconnected); setInitialState(mDisconnected); } } static HeadsetStateMachine make(BluetoothDevice device, Looper looper, HeadsetService service, static HeadsetStateMachine make(BluetoothDevice device, Looper looper, HeadsetService headsetService, AdapterService adapterService, HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) { Log.i(TAG, "make"); HeadsetStateMachine stateMachine = HeadsetStateMachine stateMachine = new HeadsetStateMachine(device, looper, service, nativeInterface, systemInterface); new HeadsetStateMachine(device, looper, headsetService, adapterService, nativeInterface, systemInterface); stateMachine.start(); stateMachine.start(); Log.i(TAG, "Created state machine " + stateMachine + " for " + device); return stateMachine; return stateMachine; } } Loading Loading @@ -302,13 +308,14 @@ public class HeadsetStateMachine extends StateMachine { // Headset is disconnecting, stop Virtual call if active. // Headset is disconnecting, stop Virtual call if active. terminateScoUsingVirtualVoiceCall(); terminateScoUsingVirtualVoiceCall(); } } mService.onConnectionStateChangedFromStateMachine(device, fromState, toState); mHeadsetService.onConnectionStateChangedFromStateMachine(device, fromState, toState); Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); } } // Should not be called from enter() method // Should not be called from enter() method Loading @@ -319,12 +326,13 @@ public class HeadsetStateMachine extends StateMachine { // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall. terminateScoUsingVirtualVoiceCall(); terminateScoUsingVirtualVoiceCall(); } } mService.onAudioStateChangedFromStateMachine(device, fromState, toState); mHeadsetService.onAudioStateChangedFromStateMachine(device, fromState, toState); Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); } } /** /** Loading Loading @@ -451,6 +459,11 @@ public class HeadsetStateMachine extends StateMachine { mWaitingForVoiceRecognition = false; mWaitingForVoiceRecognition = false; mAudioParams.clear(); mAudioParams.clear(); broadcastStateTransitions(); broadcastStateTransitions(); // Remove the state machine for unbonded devices if (mPrevState != null && mAdapterService.getBondState(mDevice) == BluetoothDevice.BOND_NONE) { getHandler().post(() -> mHeadsetService.removeStateMachine(mDevice)); } } } @Override @Override Loading Loading @@ -516,12 +529,12 @@ public class HeadsetStateMachine extends StateMachine { // Both events result in Connecting state as SLC establishment is still required // Both events result in Connecting state as SLC establishment is still required case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: if (mService.okToAcceptConnection(mDevice)) { if (mHeadsetService.okToAcceptConnection(mDevice)) { stateLogI("accept incoming connection"); stateLogI("accept incoming connection"); transitionTo(mConnecting); transitionTo(mConnecting); } else { } else { stateLogI("rejected incoming HF, priority=" + mService.getPriority(mDevice) stateLogI("rejected incoming HF, priority=" + mHeadsetService.getPriority( + " bondState=" + mDevice.getBondState()); mDevice) + " bondState=" + mAdapterService.getBondState(mDevice)); // Reject the connection and stay in Disconnected state itself // Reject the connection and stay in Disconnected state itself if (!mNativeInterface.disconnectHfp(mDevice)) { if (!mNativeInterface.disconnectHfp(mDevice)) { stateLogE("failed to disconnect"); stateLogE("failed to disconnect"); Loading Loading @@ -1219,8 +1232,8 @@ public class HeadsetStateMachine extends StateMachine { // Set active device to current active SCO device when the current active device // Set active device to current active SCO device when the current active device // is different from mCurrentDevice. This is to accommodate active device state // is different from mCurrentDevice. This is to accommodate active device state // mis-match between native and Java. // mis-match between native and Java. if (!mDevice.equals(mService.getActiveDevice())) { if (!mDevice.equals(mHeadsetService.getActiveDevice())) { mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); } } setAudioParameters(); setAudioParameters(); mSystemInterface.getAudioManager().setBluetoothScoOn(true); mSystemInterface.getAudioManager().setBluetoothScoOn(true); Loading Loading @@ -1460,7 +1473,7 @@ public class HeadsetStateMachine extends StateMachine { } } } } try { try { mService.startActivity(VOICE_COMMAND_INTENT); mHeadsetService.startActivity(VOICE_COMMAND_INTENT); } catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) { mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); 0); Loading Loading @@ -1549,7 +1562,7 @@ public class HeadsetStateMachine extends StateMachine { private synchronized void expectVoiceRecognition(BluetoothDevice device) { private synchronized void expectVoiceRecognition(BluetoothDevice device) { mWaitingForVoiceRecognition = true; mWaitingForVoiceRecognition = true; mService.setActiveDevice(device); mHeadsetService.setActiveDevice(device); sendMessageDelayed(START_VR_TIMEOUT, device, START_VR_TIMEOUT_MS); sendMessageDelayed(START_VR_TIMEOUT, device, START_VR_TIMEOUT_MS); if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { mSystemInterface.getVoiceRecognitionWakeLock().acquire(START_VR_TIMEOUT_MS); mSystemInterface.getVoiceRecognitionWakeLock().acquire(START_VR_TIMEOUT_MS); Loading @@ -1576,7 +1589,7 @@ public class HeadsetStateMachine extends StateMachine { intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + Integer.toString(companyId)); + Integer.toString(companyId)); mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); } } private void setAudioParameters() { private void setAudioParameters() { Loading Loading @@ -1720,11 +1733,11 @@ public class HeadsetStateMachine extends StateMachine { } } // Check for virtual call to terminate before sending Call Intent // Check for virtual call to terminate before sending Call Intent terminateScoUsingVirtualVoiceCall(); terminateScoUsingVirtualVoiceCall(); mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); Uri.fromParts(SCHEME_TEL, dialNumber, null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mService.startActivity(intent); mHeadsetService.startActivity(intent); // TODO(BT) continue send OK reults code after call starts // TODO(BT) continue send OK reults code after call starts // hold wait lock, start a timer, set wait call flag // hold wait lock, start a timer, set wait call flag // Get call started indication from bluetooth phone // Get call started indication from bluetooth phone Loading Loading @@ -1761,7 +1774,7 @@ public class HeadsetStateMachine extends StateMachine { if (!hasMessages(DIALING_OUT_TIMEOUT)) { if (!hasMessages(DIALING_OUT_TIMEOUT)) { return; return; } } mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0); mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0); removeMessages(DIALING_OUT_TIMEOUT); removeMessages(DIALING_OUT_TIMEOUT); } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE Loading Loading @@ -2063,7 +2076,7 @@ public class HeadsetStateMachine extends StateMachine { mSystemInterface.answerCall(device); mSystemInterface.answerCall(device); } else if (phoneState.getNumActiveCall() > 0) { } else if (phoneState.getNumActiveCall() > 0) { if (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { if (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); mNativeInterface.connectAudio(mDevice); mNativeInterface.connectAudio(mDevice); } else { } else { mSystemInterface.hangupCall(device, false); mSystemInterface.hangupCall(device, false); Loading @@ -2074,11 +2087,11 @@ public class HeadsetStateMachine extends StateMachine { log("processKeyPressed, last dial number null"); log("processKeyPressed, last dial number null"); return; return; } } mService.setActiveDevice(mDevice); mHeadsetService.setActiveDevice(mDevice); Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); Uri.fromParts(SCHEME_TEL, dialNumber, null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mService.startActivity(intent); mHeadsetService.startActivity(intent); } } } } Loading @@ -2095,7 +2108,7 @@ public class HeadsetStateMachine extends StateMachine { intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId); intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId); intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue); intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue); mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); mHeadsetService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); } } private void processAtBind(String atString, BluetoothDevice device) { private void processAtBind(String atString, BluetoothDevice device) { Loading Loading @@ -2159,7 +2172,7 @@ public class HeadsetStateMachine extends StateMachine { } } private String getCurrentDeviceName() { private String getCurrentDeviceName() { String deviceName = mDevice.getName(); String deviceName = mAdapterService.getRemoteName(mDevice); if (deviceName == null) { if (deviceName == null) { return "<unknown>"; return "<unknown>"; } } Loading @@ -2169,28 +2182,28 @@ public class HeadsetStateMachine extends StateMachine { // Accept incoming SCO only when there is in-band ringing, incoming call, // Accept incoming SCO only when there is in-band ringing, incoming call, // active call, VR activated, active VOIP call // active call, VR activated, active VOIP call private boolean isScoAcceptable() { private boolean isScoAcceptable() { if (mService.getForceScoAudio()) { if (mHeadsetService.getForceScoAudio()) { return true; return true; } } BluetoothDevice activeDevice = mService.getActiveDevice(); BluetoothDevice activeDevice = mHeadsetService.getActiveDevice(); if (!mDevice.equals(activeDevice)) { if (!mDevice.equals(activeDevice)) { Log.w(TAG, "isScoAcceptable: rejected SCO since " + mDevice Log.w(TAG, "isScoAcceptable: rejected SCO since " + mDevice + " is not the current active device " + activeDevice); + " is not the current active device " + activeDevice); return false; return false; } } if (!mService.getAudioRouteAllowed()) { if (!mHeadsetService.getAudioRouteAllowed()) { Log.w(TAG, "isScoAcceptabl: rejected SCO since audio route is not allowed"); Log.w(TAG, "isScoAcceptabl: rejected SCO since audio route is not allowed"); return false; return false; } } if (mSystemInterface.isInCall() || mVoiceRecognitionStarted) { if (mSystemInterface.isInCall() || mVoiceRecognitionStarted) { return true; return true; } } if (mSystemInterface.isRinging() && mService.isInbandRingingEnabled()) { if (mSystemInterface.isRinging() && mHeadsetService.isInbandRingingEnabled()) { return true; return true; } } Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall() Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall() + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing=" + mSystemInterface + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing=" + mSystemInterface .isRinging() + ", inbandRinging=" + mService.isInbandRingingEnabled()); .isRinging() + ", inbandRinging=" + mHeadsetService.isInbandRingingEnabled()); return false; return false; } } Loading
tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java 0 → 100644 +341 −0 File added.Preview size limit exceeded, changes collapsed. Show changes