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

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

Merge "Fix deadlock due to callbacks in ProgramList" into tm-qpr-dev

parents 5193b4cd b5c6901c
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);
    }

    /**