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

Commit d7d16b9f authored by Calvin On's avatar Calvin On Committed by Andre Eisenbach
Browse files

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
parent b0b781ab
Loading
Loading
Loading
Loading
+159 −113
Original line number Diff line number Diff line
@@ -30,8 +30,11 @@ import android.os.ParcelUuid;
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 @@ public final class BluetoothA2dp implements BluetoothProfile {

    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 =
@@ -123,24 +127,26 @@ public final class BluetoothA2dp implements BluetoothProfile {
                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                    if (!up) {
                        if (VDBG) Log.d(TAG, "Unbinding service...");
                        synchronized (mConnection) {
                        try {
                            mServiceLock.writeLock().lock();
                            mService = null;
                            mContext.unbindService(mConnection);
                        } catch (Exception re) {
                            Log.e(TAG, "", re);
                            }
                        } finally {
                            mServiceLock.writeLock().unlock();
                        }
                    } else {
                        synchronized (mConnection) {
                        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 @@ public final class BluetoothA2dp implements BluetoothProfile {
            }
        }

        synchronized (mConnection) {
            if (mService != null) {
        try {
            mServiceLock.writeLock().lock();
            if (mService != null) {
                mService = null;
                mContext.unbindService(mConnection);
            }
        } catch (Exception re) {
            Log.e(TAG, "", re);
                }
            }
        } finally {
            mServiceLock.writeLock().unlock();
        }
    }

@@ -229,18 +236,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
     */
    public boolean connect(BluetoothDevice device) {
        if (DBG) log("connect(" + device + ")");
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled() &&
                isValidDevice(device)) {
            try {
                return mService.connect(device);
            }
            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;
    }

    /**
     * Initiate disconnection from a profile
@@ -270,70 +280,82 @@ public final class BluetoothA2dp implements BluetoothProfile {
     */
    public boolean disconnect(BluetoothDevice device) {
        if (DBG) log("disconnect(" + device + ")");
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled() &&
                isValidDevice(device)) {
            try {
                return mService.disconnect(device);
            }
            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;
    }

    /**
     * {@inheritDoc}
     */
    public List<BluetoothDevice> getConnectedDevices() {
        if (VDBG) log("getConnectedDevices()");
        if (mService != null && isEnabled()) {
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()) {
                return mService.getConnectedDevices();
            }
            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>();
    }

    /**
     * {@inheritDoc}
     */
    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
        if (VDBG) log("getDevicesMatchingStates()");
        if (mService != null && isEnabled()) {
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()) {
                return mService.getDevicesMatchingConnectionStates(states);
            }
            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>();
    }

    /**
     * {@inheritDoc}
     */
    public int getConnectionState(BluetoothDevice device) {
        if (VDBG) log("getState(" + device + ")");
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()
                && isValidDevice(device)) {
            try {
                return mService.getConnectionState(device);
            }
            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;
    }

    /**
     * Set priority of the profile
@@ -352,22 +374,25 @@ public final class BluetoothA2dp implements BluetoothProfile {
     */
    public boolean setPriority(BluetoothDevice device, int priority) {
        if (DBG) log("setPriority(" + device + ", " + priority + ")");
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()
                && isValidDevice(device)) {
                if (priority != BluetoothProfile.PRIORITY_OFF &&
                    priority != BluetoothProfile.PRIORITY_ON) {
                    return false;
                }
            try {
                return mService.setPriority(device, priority);
            }
            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;
    }

    /**
     * Get the priority of the profile.
@@ -385,18 +410,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
    @RequiresPermission(Manifest.permission.BLUETOOTH)
    public int getPriority(BluetoothDevice device) {
        if (VDBG) log("getPriority(" + device + ")");
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()
                && isValidDevice(device)) {
            try {
                return mService.getPriority(device);
            }
            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;
    }

    /**
     * Checks if Avrcp device supports the absolute volume feature.
@@ -406,17 +434,20 @@ public final class BluetoothA2dp implements BluetoothProfile {
     */
    public boolean isAvrcpAbsoluteVolumeSupported() {
        if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
        if (mService != null && isEnabled()) {
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()) {
                return mService.isAvrcpAbsoluteVolumeSupported();
            }
            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;
    }

    /**
     * Tells remote device to adjust volume. Only if absolute volume is
@@ -433,16 +464,17 @@ public final class BluetoothA2dp implements BluetoothProfile {
     */
    public void adjustAvrcpAbsoluteVolume(int direction) {
        if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
        if (mService != null && isEnabled()) {
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()) {
                mService.adjustAvrcpAbsoluteVolume(direction);
                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);
                return;
            }
        } finally {
            mServiceLock.readLock().unlock();
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
    }

    /**
@@ -453,16 +485,17 @@ public final class BluetoothA2dp implements BluetoothProfile {
     */
    public void setAvrcpAbsoluteVolume(int volume) {
        if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
        if (mService != null && isEnabled()) {
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()) {
                mService.setAvrcpAbsoluteVolume(volume);
                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);
                return;
            }
        } finally {
            mServiceLock.readLock().unlock();
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
    }

    /**
@@ -473,18 +506,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
     * @param device BluetoothDevice device
     */
    public boolean isA2dpPlaying(BluetoothDevice device) {
        try {
            mServiceLock.readLock().lock();
            if (mService != null && isEnabled()
                && isValidDevice(device)) {
            try {
                return mService.isA2dpPlaying(device);
            }
            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;
    }

    /**
     * This function checks if the remote device is an AVCRP
@@ -534,7 +570,12 @@ public final class BluetoothA2dp implements BluetoothProfile {
    private final ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            if (DBG) Log.d(TAG, "Proxy object connected");
            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 final class BluetoothA2dp implements BluetoothProfile {
        }
        public void onServiceDisconnected(ComponentName className) {
            if (DBG) Log.d(TAG, "Proxy object disconnected");
            try {
                mServiceLock.writeLock().lock();
                mService = null;
            } finally {
                mServiceLock.writeLock().unlock();
            }
            if (mServiceListener != null) {
                mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
            }