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

Commit 92c7107e authored by Weilin Xu's avatar Weilin Xu Committed by Android (Google) Code Review
Browse files

Merge "Fix deadlock due to callbacks in ProgramList"

parents 3e272fd6 4ca33d65
Loading
Loading
Loading
Loading
+34 −10
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -173,38 +172,63 @@ public final class ProgramList implements AutoCloseable {
        }
    }

    void apply(@NonNull Chunk chunk) {
    void apply(Chunk chunk) {
        List<ProgramSelector.Identifier> removedList = new ArrayList<>();
        List<ProgramSelector.Identifier> changedList = new ArrayList<>();
        List<ProgramList.ListCallback> listCallbacksCopied;
        List<OnCompleteListener> onCompleteListenersCopied = new ArrayList<>();
        synchronized (mLock) {
            if (mIsClosed) return;

            mIsComplete = false;
            listCallbacksCopied = new ArrayList<>(mListCallbacks);

            if (chunk.isPurge()) {
                new HashSet<>(mPrograms.keySet()).stream().forEach(id -> removeLocked(id));
                for (ProgramSelector.Identifier id : mPrograms.keySet()) {
                    removeLocked(id, removedList);
                }
            }

            chunk.getRemoved().stream().forEach(id -> removeLocked(id));
            chunk.getModified().stream().forEach(info -> putLocked(info));
            chunk.getRemoved().stream().forEach(id -> removeLocked(id, removedList));
            chunk.getModified().stream().forEach(info -> putLocked(info, changedList));

            if (chunk.isComplete()) {
                mIsComplete = true;
                mOnCompleteListeners.forEach(cb -> cb.onComplete());
                onCompleteListenersCopied = new ArrayList<>(mOnCompleteListeners);
            }
        }

        for (int i = 0; i < removedList.size(); i++) {
            for (int cbIndex = 0; cbIndex < listCallbacksCopied.size(); cbIndex++) {
                listCallbacksCopied.get(cbIndex).onItemRemoved(removedList.get(i));
            }
        }
        for (int i = 0; i < changedList.size(); i++) {
            for (int cbIndex = 0; cbIndex < listCallbacksCopied.size(); cbIndex++) {
                listCallbacksCopied.get(cbIndex).onItemChanged(changedList.get(i));
            }
        }
        if (chunk.isComplete()) {
            for (int cbIndex = 0; cbIndex < onCompleteListenersCopied.size(); cbIndex++) {
                onCompleteListenersCopied.get(cbIndex).onComplete();
            }
        }
    }

    private void putLocked(@NonNull RadioManager.ProgramInfo value) {
    private void putLocked(RadioManager.ProgramInfo value,
            List<ProgramSelector.Identifier> changedIdentifierList) {
        ProgramSelector.Identifier key = value.getSelector().getPrimaryId();
        mPrograms.put(Objects.requireNonNull(key), value);
        ProgramSelector.Identifier sel = value.getSelector().getPrimaryId();
        mListCallbacks.forEach(cb -> cb.onItemChanged(sel));
        changedIdentifierList.add(sel);
    }

    private void removeLocked(@NonNull ProgramSelector.Identifier key) {
    private void removeLocked(ProgramSelector.Identifier key,
            List<ProgramSelector.Identifier> removedIdentifierList) {
        RadioManager.ProgramInfo removed = mPrograms.remove(Objects.requireNonNull(key));
        if (removed == null) return;
        ProgramSelector.Identifier sel = removed.getSelector().getPrimaryId();
        mListCallbacks.forEach(cb -> cb.onItemRemoved(sel));
        removedIdentifierList.add(sel);
    }

    /**