Merge "Get AdapterService while setting priority" into q-keystone-qcom-dev
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 1cc276f..63d4181 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -56,6 +56,7 @@
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
@@ -76,6 +77,7 @@
     private final Object mBtAvrcpLock = new Object();
     private final Object mActiveDeviceLock = new Object();
     private final Object mVariableLock = new Object();
+    private final ReentrantReadWriteLock mA2dpNativeInterfaceLock = new ReentrantReadWriteLock();
 
     @VisibleForTesting
     A2dpNativeInterface mA2dpNativeInterface;
@@ -174,8 +176,16 @@
         synchronized (mVariableLock) {
             mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when A2dpService starts");
+        }
+        try {
+            mA2dpNativeInterfaceLock.writeLock().lock();
             mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(),
                 "A2dpNativeInterface cannot be null when A2dpService starts");
+        } finally {
+            mA2dpNativeInterfaceLock.writeLock().unlock();
+        }
+        BluetoothCodecConfig[] OffloadCodecConfig;
+        synchronized (mVariableLock) {
             mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
             Objects.requireNonNull(mAudioManager,
                                "AudioManager cannot be null when A2dpService starts");
@@ -228,10 +238,20 @@
             // Step 6: Initialize native interface
             List<BluetoothCodecConfig> mCodecConfigOffload;
             mCodecConfigOffload = mAudioManager.getHwOffloadEncodingFormatsSupportedForA2DP();
-            BluetoothCodecConfig[] OffloadCodecConfig  = new BluetoothCodecConfig[mCodecConfigOffload.size()];
+            OffloadCodecConfig  = new BluetoothCodecConfig[mCodecConfigOffload.size()];
             OffloadCodecConfig  = mCodecConfigOffload.toArray(OffloadCodecConfig);
-             mA2dpNativeInterface.init(mMaxConnectedAudioDevices,
-                           mA2dpCodecConfig.codecConfigPriorities(),OffloadCodecConfig);
+        }
+
+        try {
+            mA2dpNativeInterfaceLock.writeLock().lock();
+            if (mA2dpNativeInterface != null)
+                mA2dpNativeInterface.init(mMaxConnectedAudioDevices,
+                        mA2dpCodecConfig.codecConfigPriorities(),OffloadCodecConfig);
+        } finally {
+            mA2dpNativeInterfaceLock.writeLock().unlock();
+        }
+
+        synchronized (mVariableLock) {
 
             // Step 7: Check if A2DP is in offload mode
             mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled();
@@ -277,10 +297,14 @@
         unregisterReceiver(mBondStateChangedReceiver);
         mBondStateChangedReceiver = null;
         // Step 6: Cleanup native interface
-        synchronized (mVariableLock) {
+        try {
+            mA2dpNativeInterfaceLock.writeLock().lock();
             if (mA2dpNativeInterface != null)
                 mA2dpNativeInterface.cleanup();
+        } finally {
+            mA2dpNativeInterfaceLock.writeLock().unlock();
         }
+
         // Step 5: Clear codec config
         mA2dpCodecConfig = null;
 
@@ -323,10 +347,16 @@
             mSetMaxConnectedAudioDevices = 1;
             // Step 1: Clear AdapterService, A2dpNativeInterface, AudioManager
             mAudioManager = null;
-            mA2dpNativeInterface = null;
             mAdapterService = null;
         }
 
+        try {
+            mA2dpNativeInterfaceLock.writeLock().lock();
+            mA2dpNativeInterface = null;
+        } finally {
+            mA2dpNativeInterfaceLock.writeLock().unlock();
+        }
+
         return true;
     }
 
@@ -720,11 +750,14 @@
            }
         }
         // Make sure the Active device in native layer is set to null and audio is off
-        synchronized (mVariableLock ) {
+        try {
+            mA2dpNativeInterfaceLock.readLock().lock();
             if (mA2dpNativeInterface != null && !mA2dpNativeInterface.setActiveDevice(null)) {
                  Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native "
                         + "layer");
             }
+        } finally {
+            mA2dpNativeInterfaceLock.readLock().unlock();
         }
     }
 
@@ -746,13 +779,18 @@
             // Set the device as the active device if currently no active device.
             setActiveDevice(device);
         }
-        synchronized (mVariableLock) {
+
+        try {
+            mA2dpNativeInterfaceLock.readLock().lock();
             if (mA2dpNativeInterface != null &&
                !mA2dpNativeInterface.setSilenceDevice(device, silence)) {
                 Log.e(TAG, "Cannot set " + device + " silence mode " + silence + " in native layer");
                 return false;
             }
+        } finally {
+            mA2dpNativeInterfaceLock.readLock().unlock();
         }
+
         return true;
     }
 
@@ -802,19 +840,21 @@
         boolean tws_switch = false;
         Log.w(TAG, "setActiveDevice(" + device + "): previous is " + previousActiveDevice);
 
+        if (device == null) {
+            // Remove active device and continue playing audio only if necessary.
+            removeActiveDevice(false);
+            synchronized(mBtAvrcpLock) {
+                if(mAvrcp_ext != null)
+                    mAvrcp_ext.setActiveDevice(device);
+            }
+            return true;
+        }
+
         synchronized (mBtA2dpLock) {
             BATService mBatService = BATService.getBATService();
             isBAActive = (mBatService != null) && (mBatService.isBATActive());
             Log.d(TAG," setActiveDevice: BA active " + isBAActive);
 
-            if (device == null) {
-                // Remove active device and continue playing audio only if necessary.
-                removeActiveDevice(false);
-                if(mAvrcp_ext != null)
-                    mAvrcp_ext.setActiveDevice(device);
-                return true;
-            }
-
             A2dpStateMachine sm = mStateMachines.get(device);
             deviceChanged = !Objects.equals(device, mActiveDevice);
             if (!deviceChanged) {
@@ -856,13 +896,18 @@
             Log.w(TAG, "setActiveDevice coming out of mutex lock");
         }
 
-        synchronized (mVariableLock) {
+        try {
+            mA2dpNativeInterfaceLock.readLock().lock();
             if (mA2dpNativeInterface != null && !mA2dpNativeInterface.setActiveDevice(device)) {
                 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer");
                 return false;
             }
+
+        } finally {
+            mA2dpNativeInterfaceLock.readLock().unlock();
         }
 
+
         updateAndBroadcastActiveDevice(device);
         Log.d(TAG, "setActiveDevice(" + device + "): completed");
 
@@ -1299,7 +1344,8 @@
                 if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
                     switch (stackEvent.valueInt) {
                         case A2dpStackEvent.CONNECTION_STATE_CONNECTED:
-                        case A2dpStackEvent.CONNECTION_STATE_CONNECTING:
+                        case A2dpStackEvent.CONNECTION_STATE_CONNECTING: {
+                            boolean connectionAllowed;
                             synchronized (mVariableLock) {
                                 // Create a new state machine only when connecting to a device
                                 if (mAdapterService != null && mAdapterService.isVendorIntfEnabled())
@@ -1308,15 +1354,24 @@
                                     sm = getOrCreateStateMachine(device);
                                     break;
                                 }
-                                if (!connectionAllowedCheckMaxDevices(device) && mA2dpNativeInterface != null) {
-                                    Log.e(TAG, "Cannot connect to " + device
-                                            + " : too many connected devices");
-                                    mA2dpNativeInterface.disconnectA2dp(device);
-                                    return;
+                                connectionAllowed = connectionAllowedCheckMaxDevices(device);
+                            }
+                            if (!connectionAllowed) {
+                                Log.e(TAG, "Cannot connect to " + device
+                                        + " : too many connected devices");
+                                try {
+                                    mA2dpNativeInterfaceLock.readLock().lock();
+                                    if (mA2dpNativeInterface != null) {
+                                        mA2dpNativeInterface.disconnectA2dp(device);
+                                        return;
+                                    }
+                                } finally {
+                                    mA2dpNativeInterfaceLock.readLock().unlock();
                                 }
                             }
                             sm = getOrCreateStateMachine(device);
                             break;
+                        }
                         default:
                             synchronized (mVariableLock) {
                                 if (mAdapterService!= null && mAdapterService.isVendorIntfEnabled() &&
diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index c7eb53c..beff7c8 100644
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -86,18 +86,7 @@
     private CopyOnWriteArrayList<BluetoothDevice> mBondedDevices =
             new CopyOnWriteArrayList<BluetoothDevice>();
 
-    private final class ProfilesConnectionState {
-        public int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
-
-        ProfilesConnectionState(int connecting, int connected, int disconnecting) {
-          mProfilesConnecting    = connecting;
-          mProfilesConnected     = connected;
-          mProfilesDisconnecting = disconnecting;
-        }
-    }
-    private final HashMap<BluetoothDevice, ProfilesConnectionState> mDevicesConnectionState =
-            new HashMap<>();
-
+    private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
     private final HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState =
             new HashMap<>();
 
@@ -229,7 +218,6 @@
 
     public void init(RemoteDevices remoteDevices) {
         mProfileConnectionState.clear();
-        mDevicesConnectionState.clear();
         mRemoteDevices = remoteDevices;
 
         // Get default max connected audio devices from config.xml in frameworks/base/core
@@ -286,7 +274,6 @@
         }
         mService = null;
         mBondedDevices.clear();
-        mDevicesConnectionState.clear();
     }
 
     @Override
@@ -800,10 +787,6 @@
                     debugLog("Adding bonded device:" + device);
                     mBondedDevices.add(device);
                 }
-                if (!mDevicesConnectionState.containsKey(device)) {
-                    debugLog("Adding connection state:" + device);
-                    mDevicesConnectionState.put(device, new ProfilesConnectionState(0, 0, 0));
-                }
             } else if (state == BluetoothDevice.BOND_NONE) {
                 // remove device from list
                 if (mBondedDevices.remove(device)) {
@@ -811,10 +794,6 @@
                 } else {
                     debugLog("Failed to remove device: " + device);
                 }
-                if (mDevicesConnectionState.containsKey(device)) {
-                    debugLog("Removing connection state:" + device);
-                    mDevicesConnectionState.remove(device);
-                }
             }
         } catch (Exception ee) {
             Log.w(TAG, "onBondStateChanged: Exception ", ee);
@@ -889,7 +868,7 @@
 
             try {
                 validateConnectionState =
-                   updateCountersAndCheckForConnectionStateChange(device, state, prevState);
+                   updateCountersAndCheckForConnectionStateChange(state, prevState);
             } catch (IllegalStateException ee) {
                 Log.w(TAG, "ADAPTER_CONNECTION_STATE_CHANGE: unexpected transition for profile="
                         + profile + ", " + prevState + " -> " + state);
@@ -953,45 +932,33 @@
         }
     }
 
-    private boolean updateCountersAndCheckForConnectionStateChange(BluetoothDevice device,
-            int state, int prevState) {
-        if(!mDevicesConnectionState.containsKey(device)) {
-            Log.e(TAG, "Can't find device connection record, adding new one: " + device);
-            mDevicesConnectionState.put(device, new ProfilesConnectionState(0, 0, 0));
-        }
-        ProfilesConnectionState connstate = mDevicesConnectionState.get(device);
-
-        Log.e(TAG, "prevState=" + prevState + " -> State=" + state +
-            " mProfilesConnecting=" + connstate.mProfilesConnecting +
-            " mProfilesConnected=" + connstate.mProfilesConnected +
-            " mProfilesDisconnecting=" + connstate.mProfilesDisconnecting);
-
+    private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
         switch (prevState) {
             case BluetoothProfile.STATE_CONNECTING:
-                if (connstate.mProfilesConnecting > 0) {
-                    connstate.mProfilesConnecting--;
+                if (mProfilesConnecting > 0) {
+                    mProfilesConnecting--;
                 } else {
-                    Log.e(TAG, "mProfilesConnecting " + connstate.mProfilesConnecting);
+                    Log.e(TAG, "mProfilesConnecting " + mProfilesConnecting);
                     throw new IllegalStateException(
                             "Invalid state transition, " + prevState + " -> " + state);
                 }
                 break;
 
             case BluetoothProfile.STATE_CONNECTED:
-                if (connstate.mProfilesConnected > 0) {
-                    connstate.mProfilesConnected--;
+                if (mProfilesConnected > 0) {
+                    mProfilesConnected--;
                 } else {
-                    Log.e(TAG, "mProfilesConnected " + connstate.mProfilesConnected);
+                    Log.e(TAG, "mProfilesConnected " + mProfilesConnected);
                     throw new IllegalStateException(
                             "Invalid state transition, " + prevState + " -> " + state);
                 }
                 break;
 
             case BluetoothProfile.STATE_DISCONNECTING:
-                if (connstate.mProfilesDisconnecting > 0) {
-                    connstate.mProfilesDisconnecting--;
+                if (mProfilesDisconnecting > 0) {
+                    mProfilesDisconnecting--;
                 } else {
-                    Log.e(TAG, "mProfilesDisconnecting " + connstate.mProfilesDisconnecting);
+                    Log.e(TAG, "mProfilesDisconnecting " + mProfilesDisconnecting);
                     throw new IllegalStateException(
                             "Invalid state transition, " + prevState + " -> " + state);
                 }
@@ -1000,19 +967,19 @@
 
         switch (state) {
             case BluetoothProfile.STATE_CONNECTING:
-                connstate.mProfilesConnecting++;
-                return (connstate.mProfilesConnected == 0 && connstate.mProfilesConnecting == 1);
+                mProfilesConnecting++;
+                return (mProfilesConnected == 0 && mProfilesConnecting == 1);
 
             case BluetoothProfile.STATE_CONNECTED:
-                connstate.mProfilesConnected++;
-                return (connstate.mProfilesConnected == 1);
+                mProfilesConnected++;
+                return (mProfilesConnected == 1);
 
             case BluetoothProfile.STATE_DISCONNECTING:
-                connstate.mProfilesDisconnecting++;
-                return (connstate.mProfilesConnected == 0 && connstate.mProfilesDisconnecting == 1);
+                mProfilesDisconnecting++;
+                return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
 
             case BluetoothProfile.STATE_DISCONNECTED:
-                return (connstate.mProfilesConnected == 0 && connstate.mProfilesConnecting == 0);
+                return (mProfilesConnected == 0 && mProfilesConnecting == 0);
 
             default:
                 return true;
@@ -1273,8 +1240,9 @@
             // Reset adapter and profile connection states
             setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
             mProfileConnectionState.clear();
-            mDevicesConnectionState.clear();
-
+            mProfilesConnected = 0;
+            mProfilesConnecting = 0;
+            mProfilesDisconnecting = 0;
             // adapterPropertyChangedCallback has already been received.  Set the scan mode.
             setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
             // This keeps NV up-to date on first-boot after flash.
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index 7bb15f0..d9cadd9 100644
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -988,13 +988,13 @@
             Log.w(TAG, "connect: PRIORITY_OFF, device=" + device + ", " + Utils.getUidPidString());
             return false;
         }
-        ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
-        if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
-            Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, "
-                    + Utils.getUidPidString());
-            return false;
-        }
         synchronized (mStateMachines) {
+            ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
+            if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
+                Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, "
+                    + Utils.getUidPidString());
+                return false;
+            }
             Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString());
             HeadsetStateMachine stateMachine = mStateMachines.get(device);
             if (stateMachine == null) {
@@ -1106,12 +1106,12 @@
             Log.e(TAG, "->States is null");
             return devices;
         }
-        final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
-        if (bondedDevices == null) {
-            Log.e(TAG, "->Bonded device is null");
-            return devices;
-        }
         synchronized (mStateMachines) {
+            final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
+            if (bondedDevices == null) {
+                Log.e(TAG, "->Bonded device is null");
+                return devices;
+            }
             for (BluetoothDevice device : bondedDevices) {
 
                 int connectionState = getConnectionState(device);
@@ -2253,7 +2253,7 @@
         synchronized (mStateMachines) {
             if (toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
                 //Transfer SCO is not needed for TWS+ devices
-                if (mAdapterService.isTwsPlusDevice(device) &&
+                if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) &&
                    mActiveDevice != null &&
                    mAdapterService.isTwsPlusDevice(mActiveDevice) &&
                    isAudioOn()) {
@@ -2264,7 +2264,7 @@
                     Log.w(TAG, "onAudioStateChangedFromStateMachine:"
                             + "shouldPersistAudio() returns"
                             + shouldPersistAudio());
-                    if (mAdapterService.isTwsPlusDevice(device) &&
+                    if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) &&
                                    isAudioOn()) {
                         Log.w(TAG, "TWS: Don't stop VR or VOIP");
                     } else {
@@ -2297,11 +2297,11 @@
                     if (mActiveDevice != null &&
                                  !mActiveDevice.equals(device) &&
                                  shouldPersistAudio()) {
-                       if (mAdapterService.isTwsPlusDevice(device)
+                       if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device)
                                                    && isAudioOn()) {
                            Log.d(TAG, "TWS: Wait for both eSCO closed");
                        } else {
-                           if (mAdapterService.isTwsPlusDevice(device) &&
+                           if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) &&
                                isTwsPlusActive(mActiveDevice)) {
                                /* If the device for which SCO got disconnected
                                   is a TwsPlus device and TWS+ set is active
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
index 349951c..ada8ab4 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
@@ -32,6 +32,7 @@
 
 package com.android.bluetooth.opp;
 
+import android.bluetooth.BluetoothAdapter;
 import android.content.ContentValues;
 import android.content.Context;
 import android.net.Uri;
@@ -80,12 +81,15 @@
 
     private int mNumFilesAttemptedToSend;
 
+    private BluetoothAdapter mAdapter;
+
     public BluetoothOppObexClientSession(Context context, ObexTransport transport) {
         if (transport == null) {
             throw new NullPointerException("transport is null");
         }
         mContext = context;
         mTransport = transport;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
     @Override
@@ -99,7 +103,7 @@
     }
 
     @Override
-    public void stop() {
+    public synchronized void stop() {
         if (D) {
             Log.d(TAG, "Stop!");
         }
@@ -110,7 +114,23 @@
                 if (V) {
                     Log.v(TAG, "waiting for thread to terminate");
                 }
-                mThread.join();
+                if (mAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) {
+                    Log.d(TAG, "stop, bt is turning off");
+                    mThread.join(1500);
+                    if (mThread.isAlive()) {
+                        Log.d(TAG, "stop, close the transport");
+                        mThread.interrupt();
+                        try {
+                            mTransport.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "mTransport.close error");
+                        }
+                        Log.d(TAG, "stop, close the transport, done");
+                        mThread.join();
+                    }
+                } else {
+                    mThread.join();
+                }
                 mThread = null;
             } catch (InterruptedException e) {
                 if (V) {