Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1048cdb authored by Jaikumar Ganesh's avatar Jaikumar Ganesh
Browse files

Serialize all commands for a particular profile.

Change-Id: I843ea9ab0bb2372c8316e99e8c083a9939ad774a
parent 740e39be
Loading
Loading
Loading
Loading
+35 −11
Original line number Original line Diff line number Diff line
/*
/*
 * Copyright (C) 2009 The Android Open Source Project
 * Copyright (C) 2010 The Android Open Source Project
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * you may not use this file except in compliance with the License.
@@ -54,8 +54,8 @@ import com.android.internal.util.HierarchicalStateMachine;
 * Todo(): Write tests for this class, when the Android Mock support is completed.
 * Todo(): Write tests for this class, when the Android Mock support is completed.
 * @hide
 * @hide
 */
 */
public final class BluetoothProfileConnectionState extends HierarchicalStateMachine {
public final class BluetoothDeviceProfileState extends HierarchicalStateMachine {
    private static final String TAG = "BluetoothProfileConnectionState";
    private static final String TAG = "BluetoothDeviceProfileState";
    private static final boolean DBG = true; //STOPSHIP - Change to false
    private static final boolean DBG = true; //STOPSHIP - Change to false


    public static final int CONNECT_HFP_OUTGOING = 1;
    public static final int CONNECT_HFP_OUTGOING = 1;
@@ -72,7 +72,7 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
    public static final int AUTO_CONNECT_PROFILES = 10;
    public static final int AUTO_CONNECT_PROFILES = 10;
    public static final int TRANSITION_TO_STABLE = 11;
    public static final int TRANSITION_TO_STABLE = 11;


    private static final int AUTO_CONNECT_DELAY = 8000; // 8 secs
    private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs


    private BondedDevice mBondedDevice = new BondedDevice();
    private BondedDevice mBondedDevice = new BondedDevice();
    private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree();
    private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree();
@@ -137,7 +137,22 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
      }
      }
    };
    };


    public BluetoothProfileConnectionState(Context context, String address,
    private boolean isPhoneDocked(BluetoothDevice autoConnectDevice) {
      // This works only because these broadcast intents are "sticky"
      Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
      if (i != null) {
          int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
          if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
              BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
              if (device != null && autoConnectDevice.equals(device)) {
                  return true;
              }
          }
      }
      return false;
  }

    public BluetoothDeviceProfileState(Context context, String address,
          BluetoothService service, BluetoothA2dpService a2dpService) {
          BluetoothService service, BluetoothA2dpService a2dpService) {
        super(address);
        super(address);
        mContext = context;
        mContext = context;
@@ -168,12 +183,12 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
            mHeadsetService = new BluetoothHeadset(mContext, this);
            mHeadsetService = new BluetoothHeadset(mContext, this);
        }
        }
        public void onServiceConnected() {
        public void onServiceConnected() {
            synchronized(BluetoothProfileConnectionState.this) {
            synchronized(BluetoothDeviceProfileState.this) {
                mHeadsetServiceConnected = true;
                mHeadsetServiceConnected = true;
            }
            }
        }
        }
        public void onServiceDisconnected() {
        public void onServiceDisconnected() {
            synchronized(BluetoothProfileConnectionState.this) {
            synchronized(BluetoothDeviceProfileState.this) {
                mHeadsetServiceConnected = false;
                mHeadsetServiceConnected = false;
            }
            }
        }
        }
@@ -222,16 +237,21 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
                    processCommand(UNPAIR);
                    processCommand(UNPAIR);
                    break;
                    break;
                case AUTO_CONNECT_PROFILES:
                case AUTO_CONNECT_PROFILES:
                    if (!mHeadsetServiceConnected) {
                    if (isPhoneDocked(mDevice)) {
                        // Don't auto connect to docks.
                        break;
                    } else if (!mHeadsetServiceConnected) {
                        deferMessage(message);
                        deferMessage(message);
                    } else {
                    } else {
                        if (mHeadsetService.getPriority(mDevice) ==
                        if (mHeadsetService.getPriority(mDevice) ==
                            BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
                              BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
                              !mHeadsetService.isConnected(mDevice)) {
                            mHeadsetService.connectHeadset(mDevice);
                            mHeadsetService.connectHeadset(mDevice);
                        }
                        }
                        if (mA2dpService != null &&
                        if (mA2dpService != null &&
                              mA2dpService.getSinkPriority(mDevice) ==
                              mA2dpService.getSinkPriority(mDevice) ==
                              BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
                              BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
                              mA2dpService.getConnectedSinks().length == 0) {
                            mA2dpService.connectSink(mDevice);
                            mA2dpService.connectSink(mDevice);
                        }
                        }
                    }
                    }
@@ -633,6 +653,10 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach
        return false;
        return false;
    }
    }


    /*package*/ BluetoothDevice getDevice() {
        return mDevice;
    }

    private void log(String message) {
    private void log(String message) {
        if (DBG) {
        if (DBG) {
            Log.i(TAG, "Device:" + mDevice + " Message:" + message);
            Log.i(TAG, "Device:" + mDevice + " Message:" + message);
+144 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.bluetooth;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Message;
import android.util.Log;

import com.android.internal.util.HierarchicalState;
import com.android.internal.util.HierarchicalStateMachine;

/**
 * This state machine is used to serialize the connections
 * to a particular profile. Currently, we only allow one device
 * to be connected to a particular profile.
 * States:
 *      {@link StableState} : No pending commands. Send the
 *      command to the appropriate remote device specific state machine.
 *
 *      {@link PendingCommandState} : A profile connection / disconnection
 *      command is being executed. This will result in a profile state
 *      change. Defer all commands.
 * @hide
 */

public class BluetoothProfileState extends HierarchicalStateMachine {
    private static final boolean DBG = true; // STOPSHIP - change to false.
    private static final String TAG = "BluetoothProfileState";

    public static int HFP = 0;
    public static int A2DP = 1;

    private static int TRANSITION_TO_STABLE = 100;

    private int mProfile;
    private BluetoothDevice mPendingDevice;
    private PendingCommandState mPendingCommandState = new PendingCommandState();
    private StableState mStableState = new StableState();

    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
                int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
                if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED ||
                    newState == BluetoothHeadset.STATE_DISCONNECTED)) {
                    sendMessage(TRANSITION_TO_STABLE);
                }
            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
                int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
                if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED ||
                    newState == BluetoothA2dp.STATE_DISCONNECTED)) {
                    sendMessage(TRANSITION_TO_STABLE);
                }
            }
        }
    };

    public BluetoothProfileState(Context context, int profile) {
        super("BluetoothProfileState:" + profile);
        mProfile = profile;
        addState(mStableState);
        addState(mPendingCommandState);
        setInitialState(mStableState);

        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
        context.registerReceiver(mBroadcastReceiver, filter);
    }

    private class StableState extends HierarchicalState {
        @Override
        protected void enter() {
            log("Entering Stable State");
            mPendingDevice = null;
        }

        @Override
        protected boolean processMessage(Message msg) {
            if (msg.what != TRANSITION_TO_STABLE) {
                transitionTo(mPendingCommandState);
            }
            return true;
        }
    }

    private class PendingCommandState extends HierarchicalState {
        @Override
        protected void enter() {
            log("Entering PendingCommandState State");
            dispatchMessage(getCurrentMessage());
        }

        @Override
        protected boolean processMessage(Message msg) {
            if (msg.what == TRANSITION_TO_STABLE) {
                transitionTo(mStableState);
            } else {
                dispatchMessage(msg);
            }
            return true;
        }

        private void dispatchMessage(Message msg) {
            BluetoothDeviceProfileState deviceProfileMgr =
              (BluetoothDeviceProfileState)msg.obj;
            int cmd = msg.arg1;
            if (mPendingDevice == null || mPendingDevice.equals(deviceProfileMgr.getDevice())) {
                mPendingDevice = deviceProfileMgr.getDevice();
                deviceProfileMgr.sendMessage(cmd);
            } else {
                Message deferMsg = new Message();
                deferMsg.arg1 = cmd;
                deferMsg.obj = deviceProfileMgr;
                deferMessage(deferMsg);
            }
        }
    }

    private void log(String message) {
        if (DBG) {
            Log.i(TAG, "Message:" + message);
        }
    }
}
+48 −28
Original line number Original line Diff line number Diff line
@@ -28,7 +28,8 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfileConnectionState;
import android.bluetooth.BluetoothDeviceProfileState;
import android.bluetooth.BluetoothProfileState;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetooth;
@@ -123,7 +124,9 @@ public class BluetoothService extends IBluetooth.Stub {


    private final HashMap<Integer, Integer> mServiceRecordToPid;
    private final HashMap<Integer, Integer> mServiceRecordToPid;


    private final HashMap<String, BluetoothProfileConnectionState> mProfileConnectionMgr;
    private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
    private final BluetoothProfileState mA2dpProfileState;
    private final BluetoothProfileState mHfpProfileState;


    private BluetoothA2dpService mA2dpService;
    private BluetoothA2dpService mA2dpService;
    private static String mDockAddress;
    private static String mDockAddress;
@@ -183,7 +186,12 @@ public class BluetoothService extends IBluetooth.Stub {
        mUuidIntentTracker = new ArrayList<String>();
        mUuidIntentTracker = new ArrayList<String>();
        mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
        mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
        mServiceRecordToPid = new HashMap<Integer, Integer>();
        mServiceRecordToPid = new HashMap<Integer, Integer>();
        mProfileConnectionMgr = new HashMap<String, BluetoothProfileConnectionState>();
        mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
        mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
        mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);

        mHfpProfileState.start();
        mA2dpProfileState.start();


        IntentFilter filter = new IntentFilter();
        IntentFilter filter = new IntentFilter();
        registerForAirplaneMode(filter);
        registerForAirplaneMode(filter);
@@ -1179,9 +1187,9 @@ public class BluetoothService extends IBluetooth.Stub {
        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
            return false;
            return false;
        }
        }
        BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
        BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
        if (state != null) {
        if (state != null) {
            state.sendMessage(BluetoothProfileConnectionState.UNPAIR);
            state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
            return true;
            return true;
        } else {
        } else {
            return false;
            return false;
@@ -1942,53 +1950,65 @@ public class BluetoothService extends IBluetooth.Stub {
    }
    }


    public boolean connectHeadset(String address) {
    public boolean connectHeadset(String address) {
        BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
        BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
        if (state != null) {
        if (state != null) {
          state.sendMessage(BluetoothProfileConnectionState.CONNECT_HFP_OUTGOING);
            Message msg = new Message();
            msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
            msg.obj = state;
            mHfpProfileState.sendMessage(msg);
            return true;
            return true;
        }
        }
        return false;
        return false;
    }
    }


    public boolean disconnectHeadset(String address) {
    public boolean disconnectHeadset(String address) {
        BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
        BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
        if (state != null) {
        if (state != null) {
            state.sendMessage(BluetoothProfileConnectionState.DISCONNECT_HFP_OUTGOING);
            Message msg = new Message();
            msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
            msg.obj = state;
            mHfpProfileState.sendMessage(msg);
            return true;
            return true;
        }
        }
        return false;
        return false;
    }
    }


    public boolean connectSink(String address) {
    public boolean connectSink(String address) {
        BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
        BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
        if (state != null) {
        if (state != null) {
          state.sendMessage(BluetoothProfileConnectionState.CONNECT_A2DP_OUTGOING);
            Message msg = new Message();
            msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
            msg.obj = state;
            mA2dpProfileState.sendMessage(msg);
            return true;
            return true;
        }
        }
        return false;
        return false;
    }
    }


    public boolean disconnectSink(String address) {
    public boolean disconnectSink(String address) {
        BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
        BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
        if (state != null) {
        if (state != null) {
            state.sendMessage(BluetoothProfileConnectionState.DISCONNECT_A2DP_OUTGOING);
            Message msg = new Message();
            msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
            msg.obj = state;
            mA2dpProfileState.sendMessage(msg);
            return true;
            return true;
        }
        }
        return false;
        return false;
    }
    }


    private BluetoothProfileConnectionState addProfileState(String address) {
    private BluetoothDeviceProfileState addProfileState(String address) {
        BluetoothProfileConnectionState state = mProfileConnectionMgr.get(address);
        BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
        if (state != null) return state;
        if (state != null) return state;


        state = new BluetoothProfileConnectionState(mContext, address, this, mA2dpService);
        state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
        mProfileConnectionMgr.put(address, state);
        mDeviceProfileState.put(address, state);
        state.start();
        state.start();
        return state;
        return state;
    }
    }


    private void removeProfileState(String address) {
    private void removeProfileState(String address) {
        mProfileConnectionMgr.remove(address);
        mDeviceProfileState.remove(address);
    }
    }


    private void initProfileState() {
    private void initProfileState() {
@@ -2003,20 +2023,20 @@ public class BluetoothService extends IBluetooth.Stub {


        for (String path : bonds) {
        for (String path : bonds) {
            String address = getAddressFromObjectPath(path);
            String address = getAddressFromObjectPath(path);
            BluetoothProfileConnectionState state = addProfileState(address);
            BluetoothDeviceProfileState state = addProfileState(address);
            // Allow 8 secs for SDP records to get registered.
            // Allow 8 secs for SDP records to get registered.
            Message msg = new Message();
            Message msg = new Message();
            msg.what = BluetoothProfileConnectionState.AUTO_CONNECT_PROFILES;
            msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
            state.sendMessageDelayed(msg, 8000);
            state.sendMessageDelayed(msg, 8000);
        }
        }
    }
    }


    public boolean notifyIncomingConnection(String address) {
    public boolean notifyIncomingConnection(String address) {
        BluetoothProfileConnectionState state =
        BluetoothDeviceProfileState state =
             mProfileConnectionMgr.get(address);
             mDeviceProfileState.get(address);
        if (state != null) {
        if (state != null) {
            Message msg = new Message();
            Message msg = new Message();
            msg.what = BluetoothProfileConnectionState.CONNECT_HFP_INCOMING;
            msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
            state.sendMessage(msg);
            state.sendMessage(msg);
            return true;
            return true;
        }
        }
@@ -2024,11 +2044,11 @@ public class BluetoothService extends IBluetooth.Stub {
    }
    }


    /*package*/ boolean notifyIncomingA2dpConnection(String address) {
    /*package*/ boolean notifyIncomingA2dpConnection(String address) {
       BluetoothProfileConnectionState state =
       BluetoothDeviceProfileState state =
            mProfileConnectionMgr.get(address);
            mDeviceProfileState.get(address);
       if (state != null) {
       if (state != null) {
           Message msg = new Message();
           Message msg = new Message();
           msg.what = BluetoothProfileConnectionState.CONNECT_A2DP_INCOMING;
           msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
           state.sendMessage(msg);
           state.sendMessage(msg);
           return true;
           return true;
       }
       }