Guard concurrent accesses to BluetoothA2dp service object

This fixes potential NPEs that happen on methods that access
mService after checking nullness, i.e. getConnectedDevices.

Bug: 29514788
Change-Id: Ic97054fd5a3563a374c0e863fb116c52535a6509
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index f66b5ff..353c640 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -30,8 +30,11 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 
 /**
@@ -114,7 +117,8 @@
 
     private Context mContext;
     private ServiceListener mServiceListener;
-    private IBluetoothA2dp mService;
+    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
+    @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
     private BluetoothAdapter mAdapter;
 
     final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -122,25 +126,27 @@
                 public void onBluetoothStateChange(boolean up) {
                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                     if (!up) {
-                        if (VDBG) Log.d(TAG,"Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG,"",re);
-                            }
+                        if (VDBG) Log.d(TAG, "Unbinding service...");
+                        try {
+                            mServiceLock.writeLock().lock();
+                            mService = null;
+                            mContext.unbindService(mConnection);
+                        } catch (Exception re) {
+                            Log.e(TAG, "", re);
+                        } finally {
+                            mServiceLock.writeLock().unlock();
                         }
                     } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG,"Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG,"",re);
+                        try {
+                            mServiceLock.readLock().lock();
+                            if (mService == null) {
+                                if (VDBG) Log.d(TAG,"Binding service...");
+                                doBind();
                             }
+                        } catch (Exception re) {
+                            Log.e(TAG,"",re);
+                        } finally {
+                            mServiceLock.readLock().unlock();
                         }
                     }
                 }
@@ -189,15 +195,16 @@
             }
         }
 
-        synchronized (mConnection) {
+        try {
+            mServiceLock.writeLock().lock();
             if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG,"",re);
-                }
+                mService = null;
+                mContext.unbindService(mConnection);
             }
+        } catch (Exception re) {
+            Log.e(TAG, "", re);
+        } finally {
+            mServiceLock.writeLock().unlock();
         }
     }
 
@@ -229,17 +236,20 @@
      */
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
-        if (mService != null && isEnabled() &&
-            isValidDevice(device)) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
                 return mService.connect(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return false;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return false;
     }
 
     /**
@@ -270,17 +280,20 @@
      */
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
-        if (mService != null && isEnabled() &&
-            isValidDevice(device)) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled() &&
+                isValidDevice(device)) {
                 return mService.disconnect(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return false;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return false;
     }
 
     /**
@@ -288,16 +301,19 @@
      */
     public List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
-        if (mService != null && isEnabled()) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
                 return mService.getConnectedDevices();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return new ArrayList<BluetoothDevice>();
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return new ArrayList<BluetoothDevice>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<BluetoothDevice>();
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return new ArrayList<BluetoothDevice>();
     }
 
     /**
@@ -305,16 +321,19 @@
      */
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
-        if (mService != null && isEnabled()) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
                 return mService.getDevicesMatchingConnectionStates(states);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return new ArrayList<BluetoothDevice>();
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return new ArrayList<BluetoothDevice>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<BluetoothDevice>();
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return new ArrayList<BluetoothDevice>();
     }
 
     /**
@@ -322,17 +341,20 @@
      */
     public int getConnectionState(BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
-        if (mService != null && isEnabled()
-            && isValidDevice(device)) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                && isValidDevice(device)) {
                 return mService.getConnectionState(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return BluetoothProfile.STATE_DISCONNECTED;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.STATE_DISCONNECTED;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.STATE_DISCONNECTED;
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return BluetoothProfile.STATE_DISCONNECTED;
     }
 
     /**
@@ -352,21 +374,24 @@
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        if (mService != null && isEnabled()
-            && isValidDevice(device)) {
-            if (priority != BluetoothProfile.PRIORITY_OFF &&
-                priority != BluetoothProfile.PRIORITY_ON){
-              return false;
-            }
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                && isValidDevice(device)) {
+                if (priority != BluetoothProfile.PRIORITY_OFF &&
+                    priority != BluetoothProfile.PRIORITY_ON) {
+                    return false;
+                }
                 return mService.setPriority(device, priority);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return false;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return false;
     }
 
     /**
@@ -385,17 +410,20 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
-        if (mService != null && isEnabled()
-            && isValidDevice(device)) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                && isValidDevice(device)) {
                 return mService.getPriority(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return BluetoothProfile.PRIORITY_OFF;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.PRIORITY_OFF;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.PRIORITY_OFF;
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return BluetoothProfile.PRIORITY_OFF;
     }
 
     /**
@@ -406,16 +434,19 @@
      */
     public boolean isAvrcpAbsoluteVolumeSupported() {
         if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
-        if (mService != null && isEnabled()) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
                 return mService.isAvrcpAbsoluteVolumeSupported();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
-                return false;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return false;
     }
 
     /**
@@ -433,16 +464,17 @@
      */
     public void adjustAvrcpAbsoluteVolume(int direction) {
         if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
-        if (mService != null && isEnabled()) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
                 mService.adjustAvrcpAbsoluteVolume(direction);
-                return;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
-                return;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
     }
 
     /**
@@ -453,16 +485,17 @@
      */
     public void setAvrcpAbsoluteVolume(int volume) {
         if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
-        if (mService != null && isEnabled()) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
                 mService.setAvrcpAbsoluteVolume(volume);
-                return;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
-                return;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
     }
 
     /**
@@ -473,17 +506,20 @@
      * @param device BluetoothDevice device
      */
     public boolean isA2dpPlaying(BluetoothDevice device) {
-        if (mService != null && isEnabled()
-            && isValidDevice(device)) {
-            try {
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                && isValidDevice(device)) {
                 return mService.isA2dpPlaying(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-                return false;
             }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        return false;
     }
 
     /**
@@ -534,7 +570,12 @@
     private final ServiceConnection mConnection = new ServiceConnection() {
         public void onServiceConnected(ComponentName className, IBinder service) {
             if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothA2dp.Stub.asInterface(service);
+            try {
+                mServiceLock.writeLock().lock();
+                mService = IBluetoothA2dp.Stub.asInterface(service);
+            } finally {
+                mServiceLock.writeLock().unlock();
+            }
 
             if (mServiceListener != null) {
                 mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
@@ -542,7 +583,12 @@
         }
         public void onServiceDisconnected(ComponentName className) {
             if (DBG) Log.d(TAG, "Proxy object disconnected");
-            mService = null;
+            try {
+                mServiceLock.writeLock().lock();
+                mService = null;
+            } finally {
+                mServiceLock.writeLock().unlock();
+            }
             if (mServiceListener != null) {
                 mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
             }