Merge "AVRCP Controller State without Browsing"
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index cebf767..167fc10 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -269,7 +269,13 @@
         }
     }
 
-    boolean disconnect(BluetoothDevice device) {
+    /**
+     * Disconnects A2dp for the remote bluetooth device
+     *
+     * @param device is the device with which we would like to disconnect a2dp
+     * @return true if profile disconnected, false if device not connected over a2dp
+     */
+    public boolean disconnect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         if (DBG) {
             Log.d(TAG, "disconnect(): " + device);
@@ -427,19 +433,9 @@
         }
     }
 
-    private void storeActiveDeviceVolume() {
-        // Make sure volume has been stored before been removed from active.
-        if (mFactory.getAvrcpTargetService() != null && mActiveDevice != null) {
-            mFactory.getAvrcpTargetService().storeVolumeForDevice(mActiveDevice);
-        }
-    }
-
     private void removeActiveDevice(boolean forceStopPlayingAudio) {
         BluetoothDevice previousActiveDevice = mActiveDevice;
         synchronized (mStateMachines) {
-            // Make sure volume has been store before device been remove from active.
-            storeActiveDeviceVolume();
-
             // This needs to happen before we inform the audio manager that the device
             // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why.
             updateAndBroadcastActiveDevice(null);
@@ -494,22 +490,6 @@
     }
 
     /**
-     * Early notification that Hearing Aids will be the active device. This allows the A2DP to save
-     * its volume before the Audio Service starts changing its media stream.
-     */
-    public void earlyNotifyHearingAidActive() {
-        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
-
-        synchronized (mStateMachines) {
-            // Switch active device from A2DP to Hearing Aids.
-            if (DBG) {
-                Log.d(TAG, "earlyNotifyHearingAidActive: Save volume for " + mActiveDevice);
-            }
-            storeActiveDeviceVolume();
-        }
-    }
-
-    /**
      * Set the active device.
      *
      * @param device the active device
@@ -548,14 +528,6 @@
             codecStatus = sm.getCodecStatus();
 
             boolean deviceChanged = !Objects.equals(device, mActiveDevice);
-            if (deviceChanged) {
-                // Switch from one A2DP to another A2DP device
-                if (DBG) {
-                    Log.d(TAG, "Switch A2DP devices to " + device + " from " + mActiveDevice);
-                }
-                storeActiveDeviceVolume();
-            }
-
             // This needs to happen before we inform the audio manager that the device
             // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why.
             updateAndBroadcastActiveDevice(device);
diff --git a/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
index 61ed75e..1a2bba7 100644
--- a/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
+++ b/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
@@ -243,24 +243,6 @@
     }
 
     /**
-     * Store the current system volume for a device in order to be retrieved later.
-     */
-    public void storeVolumeForDevice(BluetoothDevice device) {
-        if (device == null) return;
-
-        List<BluetoothDevice> HAActiveDevices = null;
-        if (mFactory.getHearingAidService() != null) {
-            HAActiveDevices = mFactory.getHearingAidService().getActiveDevices();
-        }
-        if (HAActiveDevices != null
-                && (HAActiveDevices.get(0) != null || HAActiveDevices.get(1) != null)) {
-            Log.d(TAG, "Do not store volume when Hearing Aid devices is active");
-            return;
-        }
-        mVolumeManager.storeVolumeForDevice(device);
-    }
-
-    /**
      * Remove the stored volume for a device.
      */
     public void removeStoredVolumeForDevice(BluetoothDevice device) {
diff --git a/src/com/android/bluetooth/btservice/Config.java b/src/com/android/bluetooth/btservice/Config.java
index 2b1f46c..0ec5b47 100644
--- a/src/com/android/bluetooth/btservice/Config.java
+++ b/src/com/android/bluetooth/btservice/Config.java
@@ -127,8 +127,8 @@
                 Log.v(TAG, "Adding " + config.mClass.getSimpleName());
                 profiles.add(config.mClass);
             }
-            sSupportedProfiles = profiles.toArray(new Class[profiles.size()]);
         }
+        sSupportedProfiles = profiles.toArray(new Class[profiles.size()]);
     }
 
     static Class[] getSupportedProfiles() {
diff --git a/src/com/android/bluetooth/hearingaid/HearingAidService.java b/src/com/android/bluetooth/hearingaid/HearingAidService.java
index f29d9b9..3647a65 100644
--- a/src/com/android/bluetooth/hearingaid/HearingAidService.java
+++ b/src/com/android/bluetooth/hearingaid/HearingAidService.java
@@ -33,7 +33,6 @@
 
 import com.android.bluetooth.BluetoothMetricsProto;
 import com.android.bluetooth.Utils;
-import com.android.bluetooth.a2dp.A2dpService;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.MetricsLogger;
 import com.android.bluetooth.btservice.ProfileService;
@@ -221,7 +220,13 @@
         sHearingAidService = instance;
     }
 
-    boolean connect(BluetoothDevice device) {
+    /**
+     * Connects the hearing aid profile to the passed in device
+     *
+     * @param device is the device with which we will connect the hearing aid profile
+     * @return true if hearing aid profile successfully connected, false otherwise
+     */
+    public boolean connect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         if (DBG) {
             Log.d(TAG, "connect(): " + device);
@@ -281,7 +286,13 @@
         return true;
     }
 
-    boolean disconnect(BluetoothDevice device) {
+    /**
+     * Disconnects hearing aid profile for the passed in device
+     *
+     * @param device is the device with which we want to disconnected the hearing aid profile
+     * @return true if hearing aid profile successfully disconnected, false otherwise
+     */
+    public boolean disconnect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         if (DBG) {
             Log.d(TAG, "disconnect(): " + device);
@@ -505,15 +516,6 @@
             Long deviceHiSyncId = mDeviceHiSyncIdMap.getOrDefault(device,
                     BluetoothHearingAid.HI_SYNC_ID_INVALID);
             if (deviceHiSyncId != mActiveDeviceHiSyncId) {
-                // Give an early notification to A2DP that active device is being switched
-                // to Hearing Aids before the Audio Service.
-                final A2dpService a2dpService = mFactory.getA2dpService();
-                if (a2dpService != null) {
-                    if (DBG) {
-                        Log.d(TAG, "earlyNotifyHearingAidActive for " + device);
-                    }
-                    a2dpService.earlyNotifyHearingAidActive();
-                }
                 mActiveDeviceHiSyncId = deviceHiSyncId;
                 reportActiveDevice(device);
             }
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index 1dbafd0..8f9adae 100644
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -729,7 +729,13 @@
         return true;
     }
 
-    boolean disconnect(BluetoothDevice device) {
+    /**
+     * Disconnects hfp from the passed in device
+     *
+     * @param device is the device with which we will disconnect hfp
+     * @return true if hfp is disconnected, false if the device is not connected
+     */
+    public boolean disconnect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
         Log.i(TAG, "disconnect: device=" + device + ", " + Utils.getUidPidString());
         synchronized (mStateMachines) {
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index 474d332..e7412fe 100644
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -491,7 +491,13 @@
         return true;
     }
 
-    boolean disconnect(BluetoothDevice device) {
+    /**
+     * Disconnects hfp client for the remote bluetooth device
+     *
+     * @param device is the device with which we are attempting to disconnect the profile
+     * @return true if hfp client profile successfully disconnected, false otherwise
+     */
+    public boolean disconnect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         HeadsetClientStateMachine sm = getStateMachine(device);
         if (sm == null) {
diff --git a/src/com/android/bluetooth/hid/HidDeviceService.java b/src/com/android/bluetooth/hid/HidDeviceService.java
index eb0de8e..b2d7e41 100644
--- a/src/com/android/bluetooth/hid/HidDeviceService.java
+++ b/src/com/android/bluetooth/hid/HidDeviceService.java
@@ -592,7 +592,13 @@
                 && mHidDeviceNativeInterface.unplug();
     }
 
-    synchronized boolean connect(BluetoothDevice device) {
+    /**
+     * Connects the Hid device profile for the remote bluetooth device
+     *
+     * @param device is the device with which we would like to connect the hid device profile
+     * @return true if the connection is successful, false otherwise
+     */
+    public synchronized boolean connect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         if (DBG) {
             Log.d(TAG, "connect(): device=" + device);
@@ -601,7 +607,13 @@
         return checkCallingUid() && mHidDeviceNativeInterface.connect(device);
     }
 
-    synchronized boolean disconnect(BluetoothDevice device) {
+    /**
+     * Disconnects the hid device profile for the remote bluetooth device
+     *
+     * @param device is the device with which we would like to disconnect the hid device profile
+     * @return true if the disconnection is successful, false otherwise
+     */
+    public synchronized boolean disconnect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         if (DBG) {
             Log.d(TAG, "disconnect(): device=" + device);
@@ -701,7 +713,15 @@
         sHidDeviceService = instance;
     }
 
-    int getConnectionState(BluetoothDevice device) {
+    /**
+     * Gets the connections state for the hid device profile for the passed in device
+     *
+     * @param device is the device whose conenction state we want to verify
+     * @return current connection state, one of {@link BluetoothProfile#STATE_DISCONNECTED},
+     * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or
+     * {@link BluetoothProfile#STATE_DISCONNECTING}
+     */
+    public int getConnectionState(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (mHidDevice != null && mHidDevice.equals(device)) {
             return mHidDeviceState;
diff --git a/src/com/android/bluetooth/hid/HidHostService.java b/src/com/android/bluetooth/hid/HidHostService.java
index c378f8e..2e0019c 100644
--- a/src/com/android/bluetooth/hid/HidHostService.java
+++ b/src/com/android/bluetooth/hid/HidHostService.java
@@ -468,7 +468,14 @@
     ;
 
     //APIs
-    boolean connect(BluetoothDevice device) {
+
+    /**
+     * Connects the hid host profile for the passed in device
+     *
+     * @param device is the device with which to connect the hid host profile
+     * @return true if connection is successful, false otherwise
+     */
+    public boolean connect(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "connect: " + device.getAddress());
         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (getConnectionState(device) != BluetoothHidHost.STATE_DISCONNECTED) {
@@ -485,7 +492,13 @@
         return true;
     }
 
-    boolean disconnect(BluetoothDevice device) {
+    /**
+     * Disconnects the hid host profile from the passed in device
+     *
+     * @param device is the device with which to disconnect the hid host profile
+     * @return true
+     */
+    public boolean disconnect(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "disconnect: " + device.getAddress());
         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT, device);
diff --git a/src/com/android/bluetooth/map/BluetoothMapService.java b/src/com/android/bluetooth/map/BluetoothMapService.java
index 71942f0..1d651ba 100644
--- a/src/com/android/bluetooth/map/BluetoothMapService.java
+++ b/src/com/android/bluetooth/map/BluetoothMapService.java
@@ -509,7 +509,12 @@
         }
     }
 
-    void disconnect(BluetoothDevice device) {
+    /**
+     * Disconnects MAP from the supplied device
+     *
+     * @param device is the device on which we want to disconnect MAP
+     */
+    public void disconnect(BluetoothDevice device) {
         mSessionStatusHandler.sendMessage(
                 mSessionStatusHandler.obtainMessage(DISCONNECT_MAP, 0, 0, device));
     }
@@ -563,7 +568,14 @@
         return deviceList;
     }
 
-    int getConnectionState(BluetoothDevice device) {
+    /**
+     * Gets the connection state of MAP with the passed in device.
+     *
+     * @param device is the device whose connection state we are querying
+     * @return {@link BluetoothProfile#STATE_CONNECTED} if MAP is connected to this device,
+     * {@link BluetoothProfile#STATE_DISCONNECTED} otherwise
+     */
+    public int getConnectionState(BluetoothDevice device) {
         synchronized (this) {
             if (getState() == BluetoothMap.STATE_CONNECTED && getRemoteDevice().equals(device)) {
                 return BluetoothProfile.STATE_CONNECTED;
@@ -1197,18 +1209,6 @@
         }
 
         @Override
-        public boolean connect(BluetoothDevice device) {
-            if (VERBOSE) {
-                Log.v(TAG, "connect()");
-            }
-            BluetoothMapService service = getService();
-            if (service == null) {
-                return false;
-            }
-            return false;
-        }
-
-        @Override
         public boolean disconnect(BluetoothDevice device) {
             if (VERBOSE) {
                 Log.v(TAG, "disconnect()");
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/src/com/android/bluetooth/pbap/BluetoothPbapService.java
index e7dba2a..4ed499e 100644
--- a/src/com/android/bluetooth/pbap/BluetoothPbapService.java
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapService.java
@@ -417,7 +417,15 @@
         }
     }
 
-    int getConnectionState(BluetoothDevice device) {
+    /**
+     * Get the current connection state of PBAP with the passed in device
+     *
+     * @param device is the device whose connection state to PBAP we are trying to get
+     * @return current connection state, one of {@link BluetoothProfile#STATE_DISCONNECTED},
+     * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or
+     * {@link BluetoothProfile#STATE_DISCONNECTING}
+     */
+    public int getConnectionState(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (mPbapStateMachineMap == null) {
             return BluetoothProfile.STATE_DISCONNECTED;
diff --git a/src/com/android/bluetooth/pbapclient/PbapClientService.java b/src/com/android/bluetooth/pbapclient/PbapClientService.java
index f150cdd..16b02e1 100644
--- a/src/com/android/bluetooth/pbapclient/PbapClientService.java
+++ b/src/com/android/bluetooth/pbapclient/PbapClientService.java
@@ -317,7 +317,13 @@
         }
     }
 
-    boolean disconnect(BluetoothDevice device) {
+    /**
+     * Disconnects the pbap client profile from the passed in device
+     *
+     * @param device is the device with which we will disconnect the pbap client profile
+     * @return true if we disconnected the pbap client profile, false otherwise
+     */
+    public boolean disconnect(BluetoothDevice device) {
         if (device == null) {
             throw new IllegalArgumentException("Null device");
         }
diff --git a/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java b/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
index 78f4ad0..4492a45 100644
--- a/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
@@ -39,9 +39,7 @@
 
 import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
-import com.android.bluetooth.avrcp.AvrcpTargetService;
 import com.android.bluetooth.btservice.AdapterService;
-import com.android.bluetooth.btservice.ServiceFactory;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
@@ -78,8 +76,6 @@
     @Mock private AdapterService mAdapterService;
     @Mock private A2dpNativeInterface mA2dpNativeInterface;
     @Mock private DatabaseManager mDatabaseManager;
-    @Mock private AvrcpTargetService mAvrcpTargetService;
-    @Mock private ServiceFactory mFactory;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
@@ -98,13 +94,11 @@
         TestUtils.setAdapterService(mAdapterService);
         doReturn(MAX_CONNECTED_AUDIO_DEVICES).when(mAdapterService).getMaxConnectedAudioDevices();
         doReturn(false).when(mAdapterService).isQuietModeEnabled();
-        doReturn(mAvrcpTargetService).when(mFactory).getAvrcpTargetService();
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
 
         startService();
         mA2dpService.mA2dpNativeInterface = mA2dpNativeInterface;
-        mA2dpService.mFactory = mFactory;
 
         // Override the timeout value to speed up the test
         A2dpStateMachine.sConnectTimeoutMs = TIMEOUT_MS;    // 1s
@@ -252,8 +246,6 @@
         });
         // Verify that setActiveDevice(null) was called during shutdown
         verify(mA2dpNativeInterface).setActiveDevice(null);
-        // Verify that storeVolumeForDevice(mTestDevice) was called during shutdown
-        verify(mAvrcpTargetService).storeVolumeForDevice(mTestDevice);
         // Try to restart the service. Note: must be done on the main thread.
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             public void run() {
@@ -796,13 +788,11 @@
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
         Assert.assertTrue(mA2dpService.setSilenceMode(mTestDevice, true));
         verify(mA2dpNativeInterface).setSilenceDevice(mTestDevice, true);
-        verify(mAvrcpTargetService).storeVolumeForDevice(mTestDevice);
         Assert.assertNull(mA2dpService.getActiveDevice());
 
         // Test whether active device been resumeed after disable silence mode.
         Assert.assertTrue(mA2dpService.setSilenceMode(mTestDevice, false));
         verify(mA2dpNativeInterface).setSilenceDevice(mTestDevice, false);
-        verify(mAvrcpTargetService).storeVolumeForDevice(mTestDevice);
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
 
         // Test that active device should not be changed when silence a non-active device
@@ -810,13 +800,11 @@
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
         Assert.assertTrue(mA2dpService.setSilenceMode(otherDevice, true));
         verify(mA2dpNativeInterface).setSilenceDevice(otherDevice, true);
-        verify(mAvrcpTargetService).storeVolumeForDevice(mTestDevice);
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
 
         // Test that active device should not be changed when another device exits silence mode
         Assert.assertTrue(mA2dpService.setSilenceMode(otherDevice, false));
         verify(mA2dpNativeInterface).setSilenceDevice(otherDevice, false);
-        verify(mAvrcpTargetService).storeVolumeForDevice(mTestDevice);
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
     }
 
@@ -906,28 +894,6 @@
                 verifySupportTime, verifyNotSupportTime, verifyEnabledTime);
     }
 
-    /**
-     * Test that volume level of previous active device will be stored after set active device.
-     */
-    @Test
-    public void testStoreVolumeAfterSetActiveDevice() {
-        BluetoothDevice otherDevice = mAdapter.getRemoteDevice("05:04:03:02:01:00");
-        connectDevice(otherDevice);
-        connectDevice(mTestDevice);
-        doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).setActiveDevice(null);
-        Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice));
-
-        // Test volume stored for previous active device an adjust for current active device
-        Assert.assertTrue(mA2dpService.setActiveDevice(otherDevice));
-        verify(mAvrcpTargetService).storeVolumeForDevice(mTestDevice);
-        verify(mAvrcpTargetService).getRememberedVolumeForDevice(otherDevice);
-
-        // Test volume store for previous active device when set active device to null
-        Assert.assertTrue(mA2dpService.setActiveDevice(null));
-        verify(mAvrcpTargetService).storeVolumeForDevice(otherDevice);
-    }
-
     private void connectDevice(BluetoothDevice device) {
         connectDeviceWithCodecStatus(device, null);
     }