Merge cherrypicks of [7995446, 7995447, 7996042, 7996138, 7995448, 7995449, 7995789, 7995790, 7995450, 7996029, 7996030, 7996139, 7996140, 7996141, 7996142, 7996143, 7996144, 7995544, 7995545, 7995546, 7995547, 7995548, 7995549, 7995550, 7996145, 7996146, 7996032, 7996147, 7996148, 7996149, 7996150, 7994747, 7994748, 7995451, 7994749, 7994750, 7995966, 7995967, 7994751, 7996151, 7996152, 7996153] into pi-qpr3-b-release

Change-Id: I5234851faee3d231129dc24d704487f4dcec2123
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 64a9cad..0548cdd 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -355,24 +355,18 @@
             return false;
         }
         // Check priority and accept or reject the connection.
-        // Note: Logic can be simplified, but keeping it this way for readability
         int priority = getPriority(device);
         int bondState = mAdapterService.getBondState(device);
-        // If priority is undefined, it is likely that service discovery has not completed and peer
-        // initiated the connection. Allow this connection only if the device is bonded or bonding
-        boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED)
-                && (bondState == BluetoothDevice.BOND_BONDING
-                || bondState == BluetoothDevice.BOND_BONDED);
-        // Also allow connection when device is bonded/bonding and priority is ON/AUTO_CONNECT.
-        boolean isEnabled = (priority == BluetoothProfile.PRIORITY_ON
-                || priority == BluetoothProfile.PRIORITY_AUTO_CONNECT)
-                && (bondState == BluetoothDevice.BOND_BONDED
-                || bondState == BluetoothDevice.BOND_BONDING);
-        if (!serviceDiscoveryPending && !isEnabled) {
-            // Otherwise, reject the connection if no service discovery is pending and priority is
-            // neither PRIORITY_ON nor PRIORITY_AUTO_CONNECT
-            Log.w(TAG, "okToConnect: return false, priority=" + priority + ", bondState="
-                    + bondState);
+        // Allow this connection only if the device is bonded. Any attempt to connect while
+        // bonding would potentially lead to an unauthorized connection.
+        if (bondState != BluetoothDevice.BOND_BONDED) {
+            Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
+            return false;
+        } else if (priority != BluetoothProfile.PRIORITY_UNDEFINED
+                && priority != BluetoothProfile.PRIORITY_ON
+                && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+            // Otherwise, reject the connection if priority is not valid.
+            Log.w(TAG, "okToConnect: return false, priority=" + priority);
             return false;
         }
         return true;
diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index d594850..981a45a 100644
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -41,6 +41,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
+import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 import android.util.Pair;
 import android.util.StatsLog;
@@ -462,6 +463,7 @@
 
     // This function shall be invoked from BondStateMachine whenever the bond
     // state changes.
+    @VisibleForTesting
     void onBondStateChanged(BluetoothDevice device, int state) {
         if (device == null) {
             Log.w(TAG, "onBondStateChanged, device is null");
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index ad78955..9d8cde9 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -1817,6 +1817,18 @@
         }
     }
 
+    /**
+     * Update device UUID changed to {@link BondStateMachine}
+     *
+     * @param device remote device of interest
+     */
+    public void deviceUuidUpdated(BluetoothDevice device) {
+        // Notify BondStateMachine for SDP complete / UUID changed.
+        Message msg = mBondStateMachine.obtainMessage(BondStateMachine.UUID_UPDATE);
+        msg.obj = device;
+        mBondStateMachine.sendMessage(msg);
+    }
+
     boolean cancelBondProcess(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         byte[] addr = Utils.getBytesFromAddress(device.getAddress());
diff --git a/src/com/android/bluetooth/btservice/BondStateMachine.java b/src/com/android/bluetooth/btservice/BondStateMachine.java
index 13ef2ad..81e5aae 100644
--- a/src/com/android/bluetooth/btservice/BondStateMachine.java
+++ b/src/com/android/bluetooth/btservice/BondStateMachine.java
@@ -34,10 +34,13 @@
 import com.android.bluetooth.hfpclient.HeadsetClientService;
 import com.android.bluetooth.hid.HidHostService;
 import com.android.bluetooth.pbapclient.PbapClientService;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * This state machine handles Bluetooth Adapter State.
@@ -57,6 +60,7 @@
     static final int BONDING_STATE_CHANGE = 4;
     static final int SSP_REQUEST = 5;
     static final int PIN_REQUEST = 6;
+    static final int UUID_UPDATE = 10;
     static final int BOND_STATE_NONE = 0;
     static final int BOND_STATE_BONDING = 1;
     static final int BOND_STATE_BONDED = 2;
@@ -71,6 +75,8 @@
 
     public static final String OOBDATA = "oobdata";
 
+    @VisibleForTesting Set<BluetoothDevice> mPendingBondedDevices = new HashSet<>();
+
     private BondStateMachine(AdapterService service, AdapterProperties prop,
             RemoteDevices remoteDevices) {
         super("BondStateMachine:");
@@ -144,7 +150,11 @@
                                 + state2str(newState));
                     }
                     break;
-
+                case UUID_UPDATE:
+                    if (mPendingBondedDevices.contains(dev)) {
+                        sendIntent(dev, BluetoothDevice.BOND_BONDED, 0);
+                    }
+                    break;
                 case CANCEL_BOND:
                 default:
                     Log.e(TAG, "Received unhandled state: " + msg.what);
@@ -330,17 +340,52 @@
         mAdapterService.sendOrderedBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
     }
 
-    private void sendIntent(BluetoothDevice device, int newState, int reason) {
+    @VisibleForTesting
+    void sendIntent(BluetoothDevice device, int newState, int reason) {
         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
         int oldState = BluetoothDevice.BOND_NONE;
+        if (newState != BluetoothDevice.BOND_NONE
+                && newState != BluetoothDevice.BOND_BONDING
+                && newState != BluetoothDevice.BOND_BONDED) {
+            infoLog("Invalid bond state " + newState);
+            return;
+        }
         if (devProp != null) {
             oldState = devProp.getBondState();
         }
+        if (mPendingBondedDevices.contains(device)) {
+            mPendingBondedDevices.remove(device);
+            if (oldState == BluetoothDevice.BOND_BONDED) {
+                if (newState == BluetoothDevice.BOND_BONDING) {
+                    mAdapterProperties.onBondStateChanged(device, newState);
+                }
+                oldState = BluetoothDevice.BOND_BONDING;
+            } else {
+                // Should not enter here.
+                throw new IllegalArgumentException("Invalid old state " + oldState);
+            }
+        }
         if (oldState == newState) {
             return;
         }
+
         mAdapterProperties.onBondStateChanged(device, newState);
 
+        if (devProp != null && ((devProp.getDeviceType() == BluetoothDevice.DEVICE_TYPE_CLASSIC
+                || devProp.getDeviceType() == BluetoothDevice.DEVICE_TYPE_DUAL)
+                && newState == BluetoothDevice.BOND_BONDED && devProp.getUuids() == null)) {
+            infoLog(device + " is bonded, wait for SDP complete to broadcast bonded intent");
+            if (!mPendingBondedDevices.contains(device)) {
+                mPendingBondedDevices.add(device);
+            }
+            if (oldState == BluetoothDevice.BOND_NONE) {
+                // Broadcast NONE->BONDING for NONE->BONDED case.
+                newState = BluetoothDevice.BOND_BONDING;
+            } else {
+                return;
+            }
+        }
+
         Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
diff --git a/src/com/android/bluetooth/btservice/RemoteDevices.java b/src/com/android/bluetooth/btservice/RemoteDevices.java
index 12897fe..011177e 100644
--- a/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -177,6 +177,7 @@
         return prop.getDevice();
     }
 
+    @VisibleForTesting
     DeviceProperties addDeviceProperties(byte[] address) {
         synchronized (mDevices) {
             DeviceProperties prop = new DeviceProperties();
@@ -207,13 +208,13 @@
         private byte[] mAddress;
         private int mBluetoothClass = BluetoothClass.Device.Major.UNCATEGORIZED;
         private short mRssi;
-        private ParcelUuid[] mUuids;
-        private int mDeviceType;
         private String mAlias;
-        private int mBondState;
         private BluetoothDevice mDevice;
         private boolean mIsBondingInitiatedLocally;
         private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+        @VisibleForTesting int mBondState;
+        @VisibleForTesting int mDeviceType;
+        @VisibleForTesting ParcelUuid[] mUuids;
 
         DeviceProperties() {
             mBondState = BluetoothDevice.BOND_NONE;
@@ -545,6 +546,7 @@
                             }
                             device.mUuids = newUuids;
                             if (sAdapterService.getState() == BluetoothAdapter.STATE_ON) {
+                                sAdapterService.deviceUuidUpdated(bdDevice);
                                 sendUuidIntent(bdDevice);
                             }
                             break;
diff --git a/src/com/android/bluetooth/hearingaid/HearingAidService.java b/src/com/android/bluetooth/hearingaid/HearingAidService.java
index b30eb62..704a3d5 100644
--- a/src/com/android/bluetooth/hearingaid/HearingAidService.java
+++ b/src/com/android/bluetooth/hearingaid/HearingAidService.java
@@ -343,24 +343,18 @@
             return false;
         }
         // Check priority and accept or reject the connection.
-        // Note: Logic can be simplified, but keeping it this way for readability
         int priority = getPriority(device);
         int bondState = mAdapterService.getBondState(device);
-        // If priority is undefined, it is likely that service discovery has not completed and peer
-        // initiated the connection. Allow this connection only if the device is bonded or bonding
-        boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED)
-                && (bondState == BluetoothDevice.BOND_BONDING
-                   || bondState == BluetoothDevice.BOND_BONDED);
-        // Also allow connection when device is bonded/bonding and priority is ON/AUTO_CONNECT.
-        boolean isEnabled = (priority == BluetoothProfile.PRIORITY_ON
-                || priority == BluetoothProfile.PRIORITY_AUTO_CONNECT)
-                && (bondState == BluetoothDevice.BOND_BONDED
-                   || bondState == BluetoothDevice.BOND_BONDING);
-        if (!serviceDiscoveryPending && !isEnabled) {
-            // Otherwise, reject the connection if no service discovery is pending and priority is
-            // neither PRIORITY_ON nor PRIORITY_AUTO_CONNECT
-            Log.w(TAG, "okToConnect: return false, priority=" + priority + ", bondState="
-                    + bondState);
+        // Allow this connection only if the device is bonded. Any attempt to connect while
+        // bonding would potentially lead to an unauthorized connection.
+        if (bondState != BluetoothDevice.BOND_BONDED) {
+            Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
+            return false;
+        } else if (priority != BluetoothProfile.PRIORITY_UNDEFINED
+                && priority != BluetoothProfile.PRIORITY_ON
+                && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+            // Otherwise, reject the connection if priority is not valid.
+            Log.w(TAG, "okToConnect: return false, priority=" + priority);
             return false;
         }
         return true;
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index 2ccc1e4..6d16a6a 100644
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -1688,24 +1688,18 @@
             return false;
         }
         // Check priority and accept or reject the connection.
-        // Note: Logic can be simplified, but keeping it this way for readability
         int priority = getPriority(device);
         int bondState = mAdapterService.getBondState(device);
-        // If priority is undefined, it is likely that service discovery has not completed and peer
-        // initiated the connection. Allow this connection only if the device is bonded or bonding
-        boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED) && (
-                bondState == BluetoothDevice.BOND_BONDING
-                        || bondState == BluetoothDevice.BOND_BONDED);
-        // Also allow connection when device is bonded/bonding and priority is ON/AUTO_CONNECT.
-        boolean isEnabled = (priority == BluetoothProfile.PRIORITY_ON
-                || priority == BluetoothProfile.PRIORITY_AUTO_CONNECT) && (
-                bondState == BluetoothDevice.BOND_BONDED
-                        || bondState == BluetoothDevice.BOND_BONDING);
-        if (!serviceDiscoveryPending && !isEnabled) {
-            // Otherwise, reject the connection if no service discovery is pending and priority is
-            // neither PRIORITY_ON nor PRIORITY_AUTO_CONNECT
-            Log.w(TAG,
-                    "okToConnect: return false, priority=" + priority + ", bondState=" + bondState);
+        // Allow this connection only if the device is bonded. Any attempt to connect while
+        // bonding would potentially lead to an unauthorized connection.
+        if (bondState != BluetoothDevice.BOND_BONDED) {
+            Log.w(TAG, "okToAcceptConnection: return false, bondState=" + bondState);
+            return false;
+        } else if (priority != BluetoothProfile.PRIORITY_UNDEFINED
+                && priority != BluetoothProfile.PRIORITY_ON
+                && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+            // Otherwise, reject the connection if priority is not valid.
+            Log.w(TAG, "okToAcceptConnection: return false, priority=" + priority);
             return false;
         }
         List<BluetoothDevice> connectingConnectedDevices =
diff --git a/src/com/android/bluetooth/hid/HidHostService.java b/src/com/android/bluetooth/hid/HidHostService.java
index ff1a608..63f5206 100644
--- a/src/com/android/bluetooth/hid/HidHostService.java
+++ b/src/com/android/bluetooth/hid/HidHostService.java
@@ -26,6 +26,7 @@
 import android.os.Message;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
 import com.android.bluetooth.BluetoothMetricsProto;
@@ -793,16 +794,41 @@
         }
     }
 
-    private boolean okToConnect(BluetoothDevice device) {
+    /**
+     * Check whether can connect to a peer device.
+     * The check considers a number of factors during the evaluation.
+     *
+     * @param device the peer device to connect to
+     * @return true if connection is allowed, otherwise false
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public boolean okToConnect(BluetoothDevice device) {
         AdapterService adapterService = AdapterService.getAdapterService();
-        //check if it is inbound connection in Quiet mode, priority and Bond status
-        //to decide if its ok to allow this connection
-        if ((adapterService == null) || ((adapterService.isQuietModeEnabled()) && (mTargetDevice
-                == null)) || (BluetoothProfile.PRIORITY_OFF == getPriority(device)) || (
-                device.getBondState() == BluetoothDevice.BOND_NONE)) {
+        // Check if adapter service is null.
+        if (adapterService == null) {
+            Log.w(TAG, "okToConnect: adapter service is null");
             return false;
         }
-
+        // Check if this is an incoming connection in Quiet mode.
+        if (adapterService.isQuietModeEnabled() && mTargetDevice == null) {
+            Log.w(TAG, "okToConnect: return false as quiet mode enabled");
+            return false;
+        }
+        // Check priority and accept or reject the connection.
+        int priority = getPriority(device);
+        int bondState = adapterService.getBondState(device);
+        // Allow this connection only if the device is bonded. Any attempt to connect while
+        // bonding would potentially lead to an unauthorized connection.
+        if (bondState != BluetoothDevice.BOND_BONDED) {
+            Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
+            return false;
+        } else if (priority != BluetoothProfile.PRIORITY_UNDEFINED
+                && priority != BluetoothProfile.PRIORITY_ON
+                && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+            // Otherwise, reject the connection if priority is not valid.
+            Log.w(TAG, "okToConnect: return false, priority=" + priority);
+            return false;
+        }
         return true;
     }
 
diff --git a/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java b/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
index 6b27256..bef3bdb 100644
--- a/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
@@ -296,13 +296,13 @@
         testOkToConnectCase(mTestDevice,
                 BluetoothDevice.BOND_NONE, badPriorityValue, false);
         testOkToConnectCase(mTestDevice,
-                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_UNDEFINED, true);
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_UNDEFINED, false);
         testOkToConnectCase(mTestDevice,
                 BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_OFF, false);
         testOkToConnectCase(mTestDevice,
-                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_ON, true);
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_ON, false);
         testOkToConnectCase(mTestDevice,
-                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_AUTO_CONNECT, true);
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_AUTO_CONNECT, false);
         testOkToConnectCase(mTestDevice,
                 BluetoothDevice.BOND_BONDING, badPriorityValue, false);
         testOkToConnectCase(mTestDevice,
diff --git a/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java b/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
new file mode 100644
index 0000000..29585ff
--- /dev/null
+++ b/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2018 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 com.android.bluetooth.btservice;
+
+import static org.mockito.Mockito.*;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.Intent;
+import android.os.HandlerThread;
+import android.os.ParcelUuid;
+import android.os.UserHandle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.TestUtils;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BondStateMachineTest {
+    private static final int TEST_BOND_REASON = 0;
+    private static final byte[] TEST_BT_ADDR_BYTES = {00, 11, 22, 33, 44, 55};
+    private static final ParcelUuid[] TEST_UUIDS =
+            {ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB")};
+
+    private static final int BOND_NONE = BluetoothDevice.BOND_NONE;
+    private static final int BOND_BONDING = BluetoothDevice.BOND_BONDING;
+    private static final int BOND_BONDED = BluetoothDevice.BOND_BONDED;
+
+    private AdapterProperties mAdapterProperties;
+    private BluetoothDevice mDevice;
+    private Context mTargetContext;
+    private RemoteDevices mRemoteDevices;
+    private BondStateMachine mBondStateMachine;
+    private HandlerThread mHandlerThread;
+    private RemoteDevices.DeviceProperties mDeviceProperties;
+    private int mVerifyCount = 0;
+
+    @Mock private AdapterService mAdapterService;
+
+    @Before
+    public void setUp() throws Exception {
+        mTargetContext = InstrumentationRegistry.getTargetContext();
+        MockitoAnnotations.initMocks(this);
+        TestUtils.setAdapterService(mAdapterService);
+        mHandlerThread = new HandlerThread("BondStateMachineTestHandlerThread");
+        mHandlerThread.start();
+
+        mRemoteDevices = new RemoteDevices(mAdapterService, mHandlerThread.getLooper());
+        mRemoteDevices.reset();
+        when(mAdapterService.getResources()).thenReturn(
+                mTargetContext.getResources());
+        mAdapterProperties = new AdapterProperties(mAdapterService);
+        mAdapterProperties.init(mRemoteDevices);
+        mBondStateMachine = BondStateMachine.make(mAdapterService, mAdapterProperties,
+                mRemoteDevices);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mHandlerThread.quit();
+        TestUtils.clearAdapterService(mAdapterService);
+    }
+
+    @Test
+    public void testSendIntent() {
+        int badBondState = 42;
+        mVerifyCount = 0;
+
+        // Reset mRemoteDevices for the test.
+        mRemoteDevices.reset();
+        mDeviceProperties = mRemoteDevices.addDeviceProperties(TEST_BT_ADDR_BYTES);
+        mDevice = mDeviceProperties.getDevice();
+        Assert.assertNotNull(mDevice);
+
+        /* Classic / Dualmode test cases*/
+        // Uuid not available, mPendingBondedDevice is empty.
+        testSendIntentNoPendingDevice(BOND_NONE, BOND_NONE, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDevice(BOND_NONE, BOND_BONDING, BOND_BONDING,
+                true, BOND_NONE, BOND_BONDING);
+        testSendIntentNoPendingDevice(BOND_NONE, BOND_BONDED, BOND_BONDED,
+                true, BOND_NONE, BOND_BONDING);
+        testSendIntentNoPendingDevice(BOND_NONE, badBondState, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDevice(BOND_BONDING, BOND_NONE, BOND_NONE,
+                true, BOND_BONDING, BOND_NONE);
+        testSendIntentNoPendingDevice(BOND_BONDING, BOND_BONDING, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDevice(BOND_BONDING, BOND_BONDED, BOND_BONDED,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDevice(BOND_BONDING, badBondState, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDevice(BOND_BONDED, BOND_NONE, BOND_NONE,
+                true, BOND_BONDED, BOND_NONE);
+        testSendIntentNoPendingDevice(BOND_BONDED, BOND_BONDING, BOND_BONDING,
+                true, BOND_BONDED, BOND_BONDING);
+        testSendIntentNoPendingDevice(BOND_BONDED, BOND_BONDED, BOND_BONDED,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDevice(BOND_BONDED, badBondState, BOND_BONDED,
+                false, BOND_NONE, BOND_NONE);
+
+        // Uuid not available, mPendingBondedDevice contains a remote device.
+        testSendIntentPendingDevice(BOND_NONE, BOND_NONE, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_NONE, BOND_BONDING, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_NONE, BOND_BONDED, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_NONE, badBondState, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_BONDING, BOND_NONE, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_BONDING, BOND_BONDING, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_BONDING, BOND_BONDED, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_BONDING, badBondState, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_BONDED, BOND_NONE, BOND_NONE,
+                true, BOND_BONDING, BOND_NONE);
+        testSendIntentPendingDevice(BOND_BONDED, BOND_BONDING, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_BONDED, BOND_BONDED, BOND_BONDED,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDevice(BOND_BONDED, badBondState, BOND_BONDED,
+                false, BOND_NONE, BOND_NONE);
+
+        // Uuid available, mPendingBondedDevice is empty.
+        testSendIntentNoPendingDeviceWithUuid(BOND_NONE, BOND_NONE, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDeviceWithUuid(BOND_NONE, BOND_BONDING, BOND_BONDING,
+                true, BOND_NONE, BOND_BONDING);
+        testSendIntentNoPendingDeviceWithUuid(BOND_NONE, BOND_BONDED, BOND_BONDED,
+                true, BOND_NONE, BOND_BONDED);
+        testSendIntentNoPendingDeviceWithUuid(BOND_NONE, badBondState, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDeviceWithUuid(BOND_BONDING, BOND_NONE, BOND_NONE,
+                true, BOND_BONDING, BOND_NONE);
+        testSendIntentNoPendingDeviceWithUuid(BOND_BONDING, BOND_BONDING, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDeviceWithUuid(BOND_BONDING, BOND_BONDED, BOND_BONDED,
+                true, BOND_BONDING, BOND_BONDED);
+        testSendIntentNoPendingDeviceWithUuid(BOND_BONDING, badBondState, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDeviceWithUuid(BOND_BONDED, BOND_NONE, BOND_NONE,
+                true, BOND_BONDED, BOND_NONE);
+        testSendIntentNoPendingDeviceWithUuid(BOND_BONDED, BOND_BONDING, BOND_BONDING,
+                true, BOND_BONDED, BOND_BONDING);
+        testSendIntentNoPendingDeviceWithUuid(BOND_BONDED, BOND_BONDED, BOND_BONDED,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentNoPendingDeviceWithUuid(BOND_BONDED, badBondState, BOND_BONDED,
+                false, BOND_NONE, BOND_NONE);
+
+        // Uuid available, mPendingBondedDevice contains a remote device.
+        testSendIntentPendingDeviceWithUuid(BOND_NONE, BOND_NONE, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_NONE, BOND_BONDING, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_NONE, BOND_BONDED, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_NONE, badBondState, BOND_NONE,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_BONDING, BOND_NONE, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_BONDING, BOND_BONDING, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_BONDING, BOND_BONDED, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_BONDING, badBondState, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_BONDED, BOND_NONE, BOND_NONE,
+                true, BOND_BONDING, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_BONDED, BOND_BONDING, BOND_BONDING,
+                false, BOND_NONE, BOND_NONE);
+        testSendIntentPendingDeviceWithUuid(BOND_BONDED, BOND_BONDED, BOND_BONDED,
+                true, BOND_BONDING, BOND_BONDED);
+        testSendIntentPendingDeviceWithUuid(BOND_BONDED, badBondState, BOND_BONDED,
+                false, BOND_NONE, BOND_NONE);
+
+        /* Low energy test cases */
+        testSendIntentBle(BOND_NONE, BOND_NONE, BOND_NONE);
+        testSendIntentBle(BOND_NONE, BOND_BONDING, BOND_BONDING);
+        testSendIntentBle(BOND_NONE, BOND_BONDED, BOND_BONDED);
+        testSendIntentBle(BOND_BONDING, BOND_NONE, BOND_NONE);
+        testSendIntentBle(BOND_BONDING, BOND_BONDING, BOND_BONDING);
+        testSendIntentBle(BOND_BONDING, BOND_BONDED, BOND_BONDED);
+        testSendIntentBle(BOND_BONDED, BOND_NONE, BOND_NONE);
+        testSendIntentBle(BOND_BONDED, BOND_BONDING, BOND_BONDING);
+        testSendIntentBle(BOND_BONDED, BOND_BONDED, BOND_BONDED);
+    }
+
+    private void testSendIntentCase(int oldState, int newState, int expectedNewState,
+            boolean shouldBroadcast, int broadcastOldState, int broadcastNewState) {
+        ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class);
+
+        // Setup old state before start test.
+        mDeviceProperties.mBondState = oldState;
+
+        try {
+            mBondStateMachine.sendIntent(mDevice, newState, TEST_BOND_REASON);
+        } catch (IllegalArgumentException e) {
+            // Do nothing.
+        }
+        Assert.assertEquals(expectedNewState, mDeviceProperties.getBondState());
+
+        // Check for bond state Intent status.
+        if (shouldBroadcast) {
+            verify(mAdapterService, times(++mVerifyCount)).sendBroadcastAsUser(
+                    intentArgument.capture(), eq(UserHandle.ALL),
+                    eq(AdapterService.BLUETOOTH_PERM));
+            verifyBondStateChangeIntent(broadcastOldState, broadcastNewState,
+                    intentArgument.getValue());
+        } else {
+            verify(mAdapterService, times(mVerifyCount)).sendBroadcastAsUser(any(Intent.class),
+                    any(UserHandle.class), anyString());
+        }
+    }
+
+    private void testSendIntentNoPendingDeviceWithUuid(int oldState, int newState,
+            int expectedNewState, boolean shouldBroadcast, int broadcastOldState,
+            int broadcastNewState) {
+        // Add dummy UUID for the device.
+        mDeviceProperties.mUuids = TEST_UUIDS;
+        testSendIntentNoPendingDevice(oldState, newState, expectedNewState, shouldBroadcast,
+                broadcastOldState, broadcastNewState);
+    }
+
+    private void testSendIntentPendingDeviceWithUuid(int oldState, int newState,
+            int expectedNewState, boolean shouldBroadcast, int broadcastOldState,
+            int broadcastNewState) {
+        // Add dummy UUID for the device.
+        mDeviceProperties.mUuids = TEST_UUIDS;
+        testSendIntentPendingDevice(oldState, newState, expectedNewState, shouldBroadcast,
+                broadcastOldState, broadcastNewState);
+    }
+
+    private void testSendIntentPendingDevice(int oldState, int newState, int expectedNewState,
+            boolean shouldBroadcast, int broadcastOldState, int broadcastNewState) {
+        // Test for classic remote device.
+        mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_CLASSIC;
+        mBondStateMachine.mPendingBondedDevices.clear();
+        mBondStateMachine.mPendingBondedDevices.add(mDevice);
+        testSendIntentCase(oldState, newState, expectedNewState, shouldBroadcast,
+                broadcastOldState, broadcastNewState);
+
+        // Test for dual-mode remote device.
+        mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_DUAL;
+        mBondStateMachine.mPendingBondedDevices.clear();
+        mBondStateMachine.mPendingBondedDevices.add(mDevice);
+        testSendIntentCase(oldState, newState, expectedNewState, shouldBroadcast,
+                broadcastOldState, broadcastNewState);
+    }
+
+    private void testSendIntentNoPendingDevice(int oldState, int newState, int expectedNewState,
+            boolean shouldBroadcast, int broadcastOldState, int broadcastNewState) {
+        // Test for classic remote device.
+        mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_CLASSIC;
+        mBondStateMachine.mPendingBondedDevices.clear();
+        testSendIntentCase(oldState, newState, expectedNewState, shouldBroadcast,
+                broadcastOldState, broadcastNewState);
+
+        // Test for dual-mode remote device.
+        mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_DUAL;
+        mBondStateMachine.mPendingBondedDevices.clear();
+        testSendIntentCase(oldState, newState, expectedNewState, shouldBroadcast,
+                broadcastOldState, broadcastNewState);
+    }
+
+    private void testSendIntentBle(int oldState, int newState, int expectedNewState) {
+        // Test for low energy remote device.
+        mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_LE;
+        mBondStateMachine.mPendingBondedDevices.clear();
+        testSendIntentCase(oldState, newState, newState, (oldState != newState),
+                oldState, newState);
+    }
+
+    private void verifyBondStateChangeIntent(int oldState, int newState, Intent intent) {
+        Assert.assertNotNull(intent);
+        Assert.assertEquals(BluetoothDevice.ACTION_BOND_STATE_CHANGED, intent.getAction());
+        Assert.assertEquals(mDevice, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
+        Assert.assertEquals(newState, intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1));
+        Assert.assertEquals(oldState, intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE,
+                                                          -1));
+        if (newState == BOND_NONE) {
+            Assert.assertEquals(TEST_BOND_REASON, intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
+                                                              -1));
+        } else {
+            Assert.assertEquals(-1, intent.getIntExtra(BluetoothDevice.EXTRA_REASON, -1));
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java b/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
index bda2c15..d4a978f 100644
--- a/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
@@ -255,13 +255,13 @@
         testOkToConnectCase(mSingleDevice,
                 BluetoothDevice.BOND_NONE, badPriorityValue, false);
         testOkToConnectCase(mSingleDevice,
-                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_UNDEFINED, true);
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_UNDEFINED, false);
         testOkToConnectCase(mSingleDevice,
                 BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_OFF, false);
         testOkToConnectCase(mSingleDevice,
-                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_ON, true);
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_ON, false);
         testOkToConnectCase(mSingleDevice,
-                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_AUTO_CONNECT, true);
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_AUTO_CONNECT, false);
         testOkToConnectCase(mSingleDevice,
                 BluetoothDevice.BOND_BONDING, badPriorityValue, false);
         testOkToConnectCase(mSingleDevice,
diff --git a/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java b/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
index 188792d..e5395f2 100644
--- a/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
@@ -199,13 +199,13 @@
         testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_NONE, badPriorityValue,
                 false);
         testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING,
-                BluetoothProfile.PRIORITY_UNDEFINED, true);
+                BluetoothProfile.PRIORITY_UNDEFINED, false);
         testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING,
                 BluetoothProfile.PRIORITY_OFF, false);
         testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING,
-                BluetoothProfile.PRIORITY_ON, true);
+                BluetoothProfile.PRIORITY_ON, false);
         testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING,
-                BluetoothProfile.PRIORITY_AUTO_CONNECT, true);
+                BluetoothProfile.PRIORITY_AUTO_CONNECT, false);
         testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING, badPriorityValue,
                 false);
         testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDED,
diff --git a/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java b/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
index 349660a..939e068 100644
--- a/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
@@ -15,7 +15,11 @@
  */
 package com.android.bluetooth.hid;
 
+import static org.mockito.Mockito.*;
+
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
@@ -41,6 +45,7 @@
 public class HidHostServiceTest {
     private HidHostService mService = null;
     private BluetoothAdapter mAdapter = null;
+    private BluetoothDevice mTestDevice;
     private Context mTargetContext;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
@@ -60,6 +65,9 @@
         // Try getting the Bluetooth adapter
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         Assert.assertNotNull(mAdapter);
+
+        // Get a device for testing
+        mTestDevice = TestUtils.getTestDevice(mAdapter, 0);
     }
 
     @After
@@ -77,4 +85,79 @@
     public void testInitialize() {
         Assert.assertNotNull(HidHostService.getHidHostService());
     }
+
+    /**
+     *  Test okToConnect method using various test cases
+     */
+    @Test
+    public void testOkToConnect() {
+        int badPriorityValue = 1024;
+        int badBondState = 42;
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_NONE, BluetoothProfile.PRIORITY_UNDEFINED, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_NONE, BluetoothProfile.PRIORITY_OFF, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_NONE, BluetoothProfile.PRIORITY_ON, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_NONE, BluetoothProfile.PRIORITY_AUTO_CONNECT, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_NONE, badPriorityValue, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_UNDEFINED, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_OFF, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_ON, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDING, BluetoothProfile.PRIORITY_AUTO_CONNECT, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDING, badPriorityValue, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDED, BluetoothProfile.PRIORITY_UNDEFINED, true);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDED, BluetoothProfile.PRIORITY_OFF, false);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDED, BluetoothProfile.PRIORITY_ON, true);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDED, BluetoothProfile.PRIORITY_AUTO_CONNECT, true);
+        testOkToConnectCase(mTestDevice,
+                BluetoothDevice.BOND_BONDED, badPriorityValue, false);
+        testOkToConnectCase(mTestDevice,
+                badBondState, BluetoothProfile.PRIORITY_UNDEFINED, false);
+        testOkToConnectCase(mTestDevice,
+                badBondState, BluetoothProfile.PRIORITY_OFF, false);
+        testOkToConnectCase(mTestDevice,
+                badBondState, BluetoothProfile.PRIORITY_ON, false);
+        testOkToConnectCase(mTestDevice,
+                badBondState, BluetoothProfile.PRIORITY_AUTO_CONNECT, false);
+        testOkToConnectCase(mTestDevice,
+                badBondState, badPriorityValue, false);
+        // Restore prirority to undefined for this test device
+        Assert.assertTrue(mService.setPriority(
+                mTestDevice, BluetoothProfile.PRIORITY_UNDEFINED));
+    }
+
+    /**
+     * Helper function to test okToConnect() method.
+     *
+     * @param device test device
+     * @param bondState bond state value, could be invalid
+     * @param priority value, could be invalid
+     * @param expected expected result from okToConnect()
+     */
+    private void testOkToConnectCase(BluetoothDevice device, int bondState, int priority,
+            boolean expected) {
+        doReturn(bondState).when(mAdapterService).getBondState(device);
+        Assert.assertTrue(mService.setPriority(device, priority));
+
+        // Test when the AdapterService is in non-quiet mode.
+        doReturn(false).when(mAdapterService).isQuietModeEnabled();
+        Assert.assertEquals(expected, mService.okToConnect(device));
+
+        // Test when the AdapterService is in quiet mode.
+        doReturn(true).when(mAdapterService).isQuietModeEnabled();
+        Assert.assertEquals(false, mService.okToConnect(device));
+    }
+
 }