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

Commit b1b6f7f3 authored by Santiago Seifert's avatar Santiago Seifert
Browse files

Simplify MediaSessionRecord callback invocations

And add a comment about thread safety for the callback holders.

This change doesn't make user-impacting functional changes.

Bug: 205124386
Test: atest CtsMediaBetterTogetherTestCases CtsMediaHostTestCases
Change-Id: Ifbc97b3e7fdb958922a794afd2416dce62df9cc2
parent 7d782bbb
Loading
Loading
Loading
Loading
+54 −151
Original line number Diff line number Diff line
@@ -63,7 +63,6 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -85,7 +84,6 @@ import com.android.server.uri.UriGrantsManagerInternal;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -183,6 +181,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
    private final boolean mVolumeAdjustmentForRemoteGroupSessions;

    private final Object mLock = new Object();
    // This field is partially guarded by mLock. Writes and non-atomic iterations (for example:
    // index-based-iterations) must be guarded by mLock. But it is safe to acquire an iterator
    // without acquiring mLock.
    private final CopyOnWriteArrayList<ISessionControllerCallbackHolder>
            mControllerCallbackHolders = new CopyOnWriteArrayList<>();

@@ -770,24 +771,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
            }
            playbackState = mPlaybackState;
        }
        Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
            try {
                holder.mCallback.onPlaybackStateChanged(playbackState);
            } catch (DeadObjectException e) {
                if (deadCallbackHolders == null) {
                    deadCallbackHolders = new ArrayList<>();
                }
                deadCallbackHolders.add(holder);
                logCallbackException("Removing dead callback in pushPlaybackStateUpdate", holder,
                        e);
            } catch (RemoteException e) {
                logCallbackException("unexpected exception in pushPlaybackStateUpdate", holder, e);
            }
        }
        if (deadCallbackHolders != null) {
            removeControllerHoldersSafely(deadCallbackHolders);
        }
        performOnCallbackHolders(
                "pushPlaybackStateUpdate",
                holder -> holder.mCallback.onPlaybackStateChanged(playbackState));
    }

    private void pushMetadataUpdate() {
@@ -798,23 +784,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
            }
            metadata = mMetadata;
        }
        Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
            try {
                holder.mCallback.onMetadataChanged(metadata);
            } catch (DeadObjectException e) {
                if (deadCallbackHolders == null) {
                    deadCallbackHolders = new ArrayList<>();
                }
                deadCallbackHolders.add(holder);
                logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e);
            } catch (RemoteException e) {
                logCallbackException("unexpected exception in pushMetadataUpdate", holder, e);
            }
        }
        if (deadCallbackHolders != null) {
            removeControllerHoldersSafely(deadCallbackHolders);
        }
        performOnCallbackHolders(
                "pushMetadataUpdate", holder -> holder.mCallback.onMetadataChanged(metadata));
    }

    private void pushQueueUpdate() {
@@ -825,8 +796,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
            }
            toSend = mQueue == null ? null : new ArrayList<>(mQueue);
        }
        Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
        performOnCallbackHolders(
                "pushQueueUpdate",
                holder -> {
                    ParceledListSlice<QueueItem> parcelableQueue = null;
                    if (toSend != null) {
                        parcelableQueue = new ParceledListSlice<>(toSend);
@@ -834,22 +806,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
                        // as onQueueChanged is an async binder call.
                        parcelableQueue.setInlineCountLimit(1);
                    }

            try {
                    holder.mCallback.onQueueChanged(parcelableQueue);
            } catch (DeadObjectException e) {
                if (deadCallbackHolders == null) {
                    deadCallbackHolders = new ArrayList<>();
                }
                deadCallbackHolders.add(holder);
                logCallbackException("Removing dead callback in pushQueueUpdate", holder, e);
            } catch (RemoteException e) {
                logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
            }
        }
        if (deadCallbackHolders != null) {
            removeControllerHoldersSafely(deadCallbackHolders);
        }
                });
    }

    private void pushQueueTitleUpdate() {
@@ -860,23 +818,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
            }
            queueTitle = mQueueTitle;
        }
        Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
            try {
                holder.mCallback.onQueueTitleChanged(queueTitle);
            } catch (DeadObjectException e) {
                if (deadCallbackHolders == null) {
                    deadCallbackHolders = new ArrayList<>();
                }
                deadCallbackHolders.add(holder);
                logCallbackException("Removing dead callback in pushQueueTitleUpdate", holder, e);
            } catch (RemoteException e) {
                logCallbackException("unexpected exception in pushQueueTitleUpdate", holder, e);
            }
        }
        if (deadCallbackHolders != null) {
            removeControllerHoldersSafely(deadCallbackHolders);
        }
        performOnCallbackHolders(
                "pushQueueTitleUpdate", holder -> holder.mCallback.onQueueTitleChanged(queueTitle));
    }

    private void pushExtrasUpdate() {
@@ -887,23 +830,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
            }
            extras = mExtras;
        }
        Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
            try {
                holder.mCallback.onExtrasChanged(extras);
            } catch (DeadObjectException e) {
                if (deadCallbackHolders == null) {
                    deadCallbackHolders = new ArrayList<>();
                }
                deadCallbackHolders.add(holder);
                logCallbackException("Removing dead callback in pushExtrasUpdate", holder, e);
            } catch (RemoteException e) {
                logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
            }
        }
        if (deadCallbackHolders != null) {
            removeControllerHoldersSafely(deadCallbackHolders);
        }
        performOnCallbackHolders(
                "pushExtrasUpdate", holder -> holder.mCallback.onExtrasChanged(extras));
    }

    private void pushVolumeUpdate() {
@@ -914,23 +842,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
            }
            info = getVolumeAttributes();
        }
        Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
            try {
                holder.mCallback.onVolumeInfoChanged(info);
            } catch (DeadObjectException e) {
                if (deadCallbackHolders == null) {
                    deadCallbackHolders = new ArrayList<>();
                }
                deadCallbackHolders.add(holder);
                logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e);
            } catch (RemoteException e) {
                logCallbackException("unexpected exception in pushVolumeUpdate", holder, e);
            }
        }
        if (deadCallbackHolders != null) {
            removeControllerHoldersSafely(deadCallbackHolders);
        }
        performOnCallbackHolders(
                "pushVolumeUpdate", holder -> holder.mCallback.onVolumeInfoChanged(info));
    }

    private void pushEvent(String event, Bundle data) {
@@ -939,23 +852,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
                return;
            }
        }
        Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
            try {
                holder.mCallback.onEvent(event, data);
            } catch (DeadObjectException e) {
                if (deadCallbackHolders == null) {
                    deadCallbackHolders = new ArrayList<>();
                }
                deadCallbackHolders.add(holder);
                logCallbackException("Removing dead callback in pushEvent", holder, e);
            } catch (RemoteException e) {
                logCallbackException("unexpected exception in pushEvent", holder, e);
            }
        }
        if (deadCallbackHolders != null) {
            removeControllerHoldersSafely(deadCallbackHolders);
        }
        performOnCallbackHolders("pushEvent", holder -> holder.mCallback.onEvent(event, data));
    }

    private void pushSessionDestroyed() {
@@ -966,20 +863,37 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
                return;
            }
        }
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
            try {
        performOnCallbackHolders(
                "pushSessionDestroyed",
                holder -> {
                    holder.mCallback.asBinder().unlinkToDeath(holder.mDeathMonitor, 0);
                    holder.mCallback.onSessionDestroyed();
            } catch (NoSuchElementException e) {
                logCallbackException("error unlinking to binder death", holder, e);
            } catch (DeadObjectException e) {
                logCallbackException("Removing dead callback in pushSessionDestroyed", holder, e);
            } catch (RemoteException e) {
                logCallbackException("unexpected exception in pushSessionDestroyed", holder, e);
                });
        // After notifying clear all listeners
        synchronized (mLock) {
            mControllerCallbackHolders.clear();
        }
    }
        // After notifying clear all listeners
        removeControllerHoldersSafely(null);

    private interface ControllerCallbackCall {

        void performOn(ISessionControllerCallbackHolder holder) throws RemoteException;
    }

    private void performOnCallbackHolders(String operationName, ControllerCallbackCall call) {
        ArrayList<ISessionControllerCallbackHolder> deadCallbackHolders = new ArrayList<>();
        for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
            try {
                call.performOn(holder);
            } catch (RemoteException | NoSuchElementException exception) {
                deadCallbackHolders.add(holder);
                logCallbackException(
                        "Exception while executing: " + operationName, holder, exception);
            }
        }
        synchronized (mLock) {
            mControllerCallbackHolders.removeAll(deadCallbackHolders);
        }
    }

    private PlaybackState getStateWithUpdatedPosition() {
@@ -1027,17 +941,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
        return -1;
    }

    private void removeControllerHoldersSafely(
            Collection<ISessionControllerCallbackHolder> holders) {
        synchronized (mLock) {
            if (holders == null) {
                mControllerCallbackHolders.clear();
            } else {
                mControllerCallbackHolders.removeAll(holders);
            }
        }
    }

    private PlaybackInfo getVolumeAttributes() {
        int volumeType;
        AudioAttributes attributes;