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

Commit 5bd39684 authored by hughchen's avatar hughchen
Browse files

Fix null point exception on MediaDevice

This CL before, since we use mMediaDevices to do array compare
directly if other thread clear or change mMediaDevices will
cause the crash.

This CL will new a mMediaDevices array to do array compare to fix
this exception, also will use synchronized to protect mMediaDevices.

Bug: 155933396
Test: make -j42 RunSettingsLibRoboTests
Change-Id: I2b15a0ede7c77f2c82f32d26ff2237109808a5a2
parent 73407d65
Loading
Loading
Loading
Loading
+70 −30
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.BluetoothCallback;
@@ -62,6 +63,7 @@ public class LocalMediaManager implements BluetoothCallback {
    }

    private final Collection<DeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
    private final Object mMediaDevicesLock = new Object();
    @VisibleForTesting
    final MediaDeviceCallback mMediaDeviceCallback = new MediaDeviceCallback();

@@ -142,7 +144,14 @@ public class LocalMediaManager implements BluetoothCallback {
     * @param connectDevice the MediaDevice
     */
    public void connectDevice(MediaDevice connectDevice) {
        final MediaDevice device = getMediaDeviceById(mMediaDevices, connectDevice.getId());
        MediaDevice device = null;
        synchronized (mMediaDevicesLock) {
            device = getMediaDeviceById(mMediaDevices, connectDevice.getId());
        }
        if (device == null) {
            Log.w(TAG, "connectDevice() connectDevice not in the list!");
            return;
        }
        if (device instanceof BluetoothMediaDevice) {
            final CachedBluetoothDevice cachedDevice =
                    ((BluetoothMediaDevice) device).getCachedDevice();
@@ -181,15 +190,18 @@ public class LocalMediaManager implements BluetoothCallback {
     * Start scan connected MediaDevice
     */
    public void startScan() {
        synchronized (mMediaDevicesLock) {
            mMediaDevices.clear();
        }
        mInfoMediaManager.registerCallback(mMediaDeviceCallback);
        mInfoMediaManager.startScan();
    }

    void dispatchDeviceListUpdate() {
        Collections.sort(mMediaDevices, COMPARATOR);
        final List<MediaDevice> mediaDevices = new ArrayList<>(mMediaDevices);
        Collections.sort(mediaDevices, COMPARATOR);
        for (DeviceCallback callback : getCallbacks()) {
            callback.onDeviceListUpdate(new ArrayList<>(mMediaDevices));
            callback.onDeviceListUpdate(mediaDevices);
        }
    }

@@ -238,11 +250,13 @@ public class LocalMediaManager implements BluetoothCallback {
     * @return MediaDevice
     */
    public MediaDevice getMediaDeviceById(String id) {
        synchronized (mMediaDevicesLock) {
            for (MediaDevice mediaDevice : mMediaDevices) {
                if (TextUtils.equals(mediaDevice.getId(), id)) {
                    return mediaDevice;
                }
            }
        }
        Log.i(TAG, "Unable to find device " + id);
        return null;
    }
@@ -252,6 +266,7 @@ public class LocalMediaManager implements BluetoothCallback {
     *
     * @return MediaDevice
     */
    @Nullable
    public MediaDevice getCurrentConnectedDevice() {
        return mCurrentConnectedDevice;
    }
@@ -364,17 +379,19 @@ public class LocalMediaManager implements BluetoothCallback {
    }

    private MediaDevice updateCurrentConnectedDevice() {
        MediaDevice phoneMediaDevice = null;
        synchronized (mMediaDevicesLock) {
            for (MediaDevice device : mMediaDevices) {
                if (device instanceof BluetoothMediaDevice) {
                    if (isActiveDevice(((BluetoothMediaDevice) device).getCachedDevice())) {
                        return device;
                    }
                } else if (device instanceof PhoneMediaDevice) {
                phoneMediaDevice = device;
                    return device;
                }
            }
        return mMediaDevices.contains(phoneMediaDevice) ? phoneMediaDevice : null;
        }
        Log.w(TAG, "updateCurrentConnectedDevice() can't found current connected device");
        return null;
    }

    private boolean isActiveDevice(CachedBluetoothDevice device) {
@@ -389,17 +406,26 @@ public class LocalMediaManager implements BluetoothCallback {
    class MediaDeviceCallback implements MediaManager.MediaDeviceCallback {
        @Override
        public void onDeviceAdded(MediaDevice device) {
            boolean isAdded = false;
            synchronized (mMediaDevicesLock) {
                if (!mMediaDevices.contains(device)) {
                    mMediaDevices.add(device);
                    isAdded = true;
                }
            }

            if (isAdded) {
                dispatchDeviceListUpdate();
            }
        }

        @Override
        public void onDeviceListAdded(List<MediaDevice> devices) {
            synchronized (mMediaDevicesLock) {
                mMediaDevices.clear();
                mMediaDevices.addAll(devices);
                mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
            }

            final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
            mCurrentConnectedDevice = infoMediaDevice != null
@@ -456,31 +482,43 @@ public class LocalMediaManager implements BluetoothCallback {

        @Override
        public void onDeviceRemoved(MediaDevice device) {
            boolean isRemoved = false;
            synchronized (mMediaDevicesLock) {
                if (mMediaDevices.contains(device)) {
                    mMediaDevices.remove(device);
                    isRemoved = true;
                }
            }
            if (isRemoved) {
                dispatchDeviceListUpdate();
            }
        }

        @Override
        public void onDeviceListRemoved(List<MediaDevice> devices) {
            synchronized (mMediaDevicesLock) {
                mMediaDevices.removeAll(devices);
            }
            dispatchDeviceListUpdate();
        }

        @Override
        public void onConnectedDeviceChanged(String id) {
            MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id);
            MediaDevice connectDevice = null;
            synchronized (mMediaDevicesLock) {
                connectDevice = getMediaDeviceById(mMediaDevices, id);
            }
            connectDevice = connectDevice != null
                    ? connectDevice : updateCurrentConnectedDevice();

            mCurrentConnectedDevice = connectDevice;
            if (connectDevice != null) {
                connectDevice.setState(MediaDeviceState.STATE_CONNECTED);
            }

            mCurrentConnectedDevice = connectDevice;
                dispatchSelectedDeviceStateChanged(mCurrentConnectedDevice,
                        MediaDeviceState.STATE_CONNECTED);
            }
        }

        @Override
        public void onDeviceAttributesChanged() {
@@ -489,11 +527,13 @@ public class LocalMediaManager implements BluetoothCallback {

        @Override
        public void onRequestFailed(int reason) {
            synchronized (mMediaDevicesLock) {
                for (MediaDevice device : mMediaDevices) {
                    if (device.getState() == MediaDeviceState.STATE_CONNECTING) {
                        device.setState(MediaDeviceState.STATE_CONNECTING_FAILED);
                    }
                }
            }
            dispatchOnRequestFailed(reason);
        }
    }