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

Commit f2a1d2c5 authored by Weilin Xu's avatar Weilin Xu
Browse files

Refactor locks in TunerAdapter

Non-final member variables in TunerAdapter and
TunerCallbackAdapter are guarded by locks now. Final member
variables are moved outside the lock since they are already
thread-safe.

Bug: 247856985
Test: atest android.hardware.radio.tests.functional
Change-Id: I39204a4d5036aa66c72e54a673814a7cbbcb18b3
parent cf457f82
Loading
Loading
Loading
Loading
+99 −72
Original line number Original line Diff line number Diff line
@@ -16,12 +16,13 @@


package android.hardware.radio;
package android.hardware.radio;


import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.os.RemoteException;
import android.os.RemoteException;
import android.util.Log;
import android.util.Log;


import com.android.internal.annotations.GuardedBy;

import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.Objects;
@@ -29,28 +30,36 @@ import java.util.Objects;
/**
/**
 * Implements the RadioTuner interface by forwarding calls to radio service.
 * Implements the RadioTuner interface by forwarding calls to radio service.
 */
 */
class TunerAdapter extends RadioTuner {
final class TunerAdapter extends RadioTuner {
    private static final String TAG = "BroadcastRadio.TunerAdapter";
    private static final String TAG = "BroadcastRadio.TunerAdapter";


    @NonNull private final ITuner mTuner;
    private final ITuner mTuner;
    @NonNull private final TunerCallbackAdapter mCallback;
    private final TunerCallbackAdapter mCallback;
    private boolean mIsClosed = false;
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private boolean mIsClosed;


    private @RadioManager.Band int mBand;
    @GuardedBy("mLock")
    @RadioManager.Band
    private int mBand;


    @GuardedBy("mLock")
    private ProgramList mLegacyListProxy;
    private ProgramList mLegacyListProxy;

    @GuardedBy("mLock")
    private Map<String, String> mLegacyListFilter;
    private Map<String, String> mLegacyListFilter;


    TunerAdapter(@NonNull ITuner tuner, @NonNull TunerCallbackAdapter callback,
    TunerAdapter(ITuner tuner, TunerCallbackAdapter callback,
            @RadioManager.Band int band) {
            @RadioManager.Band int band) {
        mTuner = Objects.requireNonNull(tuner);
        mTuner = Objects.requireNonNull(tuner, "Tuner cannot be null");
        mCallback = Objects.requireNonNull(callback);
        mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
        mBand = band;
        mBand = band;
    }
    }


    @Override
    @Override
    public void close() {
    public void close() {
        synchronized (mTuner) {
        synchronized (mLock) {
            if (mIsClosed) {
            if (mIsClosed) {
                Log.v(TAG, "Tuner is already closed");
                Log.v(TAG, "Tuner is already closed");
                return;
                return;
@@ -60,8 +69,8 @@ class TunerAdapter extends RadioTuner {
                mLegacyListProxy.close();
                mLegacyListProxy.close();
                mLegacyListProxy = null;
                mLegacyListProxy = null;
            }
            }
            mCallback.close();
        }
        }
        mCallback.close();
        try {
        try {
            mTuner.close();
            mTuner.close();
        } catch (RemoteException e) {
        } catch (RemoteException e) {
@@ -71,16 +80,20 @@ class TunerAdapter extends RadioTuner {


    @Override
    @Override
    public int setConfiguration(RadioManager.BandConfig config) {
    public int setConfiguration(RadioManager.BandConfig config) {
        if (config == null) return RadioManager.STATUS_BAD_VALUE;
        if (config == null) {
            return RadioManager.STATUS_BAD_VALUE;
        }
        try {
        try {
            mTuner.setConfiguration(config);
            mTuner.setConfiguration(config);
            synchronized (mLock) {
                mBand = config.getType();
                mBand = config.getType();
            }
            return RadioManager.STATUS_OK;
            return RadioManager.STATUS_OK;
        } catch (IllegalArgumentException e) {
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Can't set configuration", e);
            Log.e(TAG, "Can't set configuration", e);
            return RadioManager.STATUS_BAD_VALUE;
            return RadioManager.STATUS_BAD_VALUE;
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Log.e(TAG, "service died", e);
            Log.e(TAG, "Service died", e);
            return RadioManager.STATUS_DEAD_OBJECT;
            return RadioManager.STATUS_DEAD_OBJECT;
        }
        }
    }
    }
@@ -94,7 +107,7 @@ class TunerAdapter extends RadioTuner {
            config[0] = mTuner.getConfiguration();
            config[0] = mTuner.getConfiguration();
            return RadioManager.STATUS_OK;
            return RadioManager.STATUS_OK;
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Log.e(TAG, "service died", e);
            Log.e(TAG, "Service died", e);
            return RadioManager.STATUS_DEAD_OBJECT;
            return RadioManager.STATUS_DEAD_OBJECT;
        }
        }
    }
    }
@@ -107,7 +120,7 @@ class TunerAdapter extends RadioTuner {
            Log.e(TAG, "Can't set muted", e);
            Log.e(TAG, "Can't set muted", e);
            return RadioManager.STATUS_ERROR;
            return RadioManager.STATUS_ERROR;
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Log.e(TAG, "service died", e);
            Log.e(TAG, "Service died", e);
            return RadioManager.STATUS_DEAD_OBJECT;
            return RadioManager.STATUS_DEAD_OBJECT;
        }
        }
        return RadioManager.STATUS_OK;
        return RadioManager.STATUS_OK;
@@ -118,7 +131,7 @@ class TunerAdapter extends RadioTuner {
        try {
        try {
            return mTuner.isMuted();
            return mTuner.isMuted();
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Log.e(TAG, "service died", e);
            Log.e(TAG, "Service died", e);
            return true;
            return true;
        }
        }
    }
    }
@@ -126,12 +139,13 @@ class TunerAdapter extends RadioTuner {
    @Override
    @Override
    public int step(int direction, boolean skipSubChannel) {
    public int step(int direction, boolean skipSubChannel) {
        try {
        try {
            mTuner.step(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
            mTuner.step(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN,
                    skipSubChannel);
        } catch (IllegalStateException e) {
        } catch (IllegalStateException e) {
            Log.e(TAG, "Can't step", e);
            Log.e(TAG, "Can't step", e);
            return RadioManager.STATUS_INVALID_OPERATION;
            return RadioManager.STATUS_INVALID_OPERATION;
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Log.e(TAG, "service died", e);
            Log.e(TAG, "Service died", e);
            return RadioManager.STATUS_DEAD_OBJECT;
            return RadioManager.STATUS_DEAD_OBJECT;
        }
        }
        return RadioManager.STATUS_OK;
        return RadioManager.STATUS_OK;
@@ -140,12 +154,13 @@ class TunerAdapter extends RadioTuner {
    @Override
    @Override
    public int scan(int direction, boolean skipSubChannel) {
    public int scan(int direction, boolean skipSubChannel) {
        try {
        try {
            mTuner.scan(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
            mTuner.scan(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN,
                    skipSubChannel);
        } catch (IllegalStateException e) {
        } catch (IllegalStateException e) {
            Log.e(TAG, "Can't scan", e);
            Log.e(TAG, "Can't scan", e);
            return RadioManager.STATUS_INVALID_OPERATION;
            return RadioManager.STATUS_INVALID_OPERATION;
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Log.e(TAG, "service died", e);
            Log.e(TAG, "Service died", e);
            return RadioManager.STATUS_DEAD_OBJECT;
            return RadioManager.STATUS_DEAD_OBJECT;
        }
        }
        return RadioManager.STATUS_OK;
        return RadioManager.STATUS_OK;
@@ -154,7 +169,11 @@ class TunerAdapter extends RadioTuner {
    @Override
    @Override
    public int tune(int channel, int subChannel) {
    public int tune(int channel, int subChannel) {
        try {
        try {
            mTuner.tune(ProgramSelector.createAmFmSelector(mBand, channel, subChannel));
            int band;
            synchronized (mLock) {
                band = mBand;
            }
            mTuner.tune(ProgramSelector.createAmFmSelector(band, channel, subChannel));
        } catch (IllegalStateException e) {
        } catch (IllegalStateException e) {
            Log.e(TAG, "Can't tune", e);
            Log.e(TAG, "Can't tune", e);
            return RadioManager.STATUS_INVALID_OPERATION;
            return RadioManager.STATUS_INVALID_OPERATION;
@@ -162,18 +181,18 @@ class TunerAdapter extends RadioTuner {
            Log.e(TAG, "Can't tune", e);
            Log.e(TAG, "Can't tune", e);
            return RadioManager.STATUS_BAD_VALUE;
            return RadioManager.STATUS_BAD_VALUE;
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Log.e(TAG, "service died", e);
            Log.e(TAG, "Service died", e);
            return RadioManager.STATUS_DEAD_OBJECT;
            return RadioManager.STATUS_DEAD_OBJECT;
        }
        }
        return RadioManager.STATUS_OK;
        return RadioManager.STATUS_OK;
    }
    }


    @Override
    @Override
    public void tune(@NonNull ProgramSelector selector) {
    public void tune(ProgramSelector selector) {
        try {
        try {
            mTuner.tune(selector);
            mTuner.tune(selector);
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


@@ -185,7 +204,7 @@ class TunerAdapter extends RadioTuner {
            Log.e(TAG, "Can't cancel", e);
            Log.e(TAG, "Can't cancel", e);
            return RadioManager.STATUS_INVALID_OPERATION;
            return RadioManager.STATUS_INVALID_OPERATION;
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Log.e(TAG, "service died", e);
            Log.e(TAG, "Service died", e);
            return RadioManager.STATUS_DEAD_OBJECT;
            return RadioManager.STATUS_DEAD_OBJECT;
        }
        }
        return RadioManager.STATUS_OK;
        return RadioManager.STATUS_OK;
@@ -196,7 +215,7 @@ class TunerAdapter extends RadioTuner {
        try {
        try {
            mTuner.cancelAnnouncement();
            mTuner.cancelAnnouncement();
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


@@ -217,11 +236,12 @@ class TunerAdapter extends RadioTuner {
    }
    }


    @Override
    @Override
    public @Nullable Bitmap getMetadataImage(int id) {
    @Nullable
    public Bitmap getMetadataImage(int id) {
        try {
        try {
            return mTuner.getImage(id);
            return mTuner.getImage(id);
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


@@ -230,49 +250,54 @@ class TunerAdapter extends RadioTuner {
        try {
        try {
            return mTuner.startBackgroundScan();
            return mTuner.startBackgroundScan();
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


    @Override
    @Override
    public @NonNull List<RadioManager.ProgramInfo>
    public List<RadioManager.ProgramInfo>
            getProgramList(@Nullable Map<String, String> vendorFilter) {
            getProgramList(@Nullable Map<String, String> vendorFilter) {
        synchronized (mTuner) {
        synchronized (mLock) {
            if (mLegacyListProxy == null || !Objects.equals(mLegacyListFilter, vendorFilter)) {
            if (mLegacyListProxy == null || !Objects.equals(mLegacyListFilter, vendorFilter)) {
                Log.i(TAG, "Program list filter has changed, requesting new list");
                Log.i(TAG, "Program list filter has changed, requesting new list");
                mLegacyListProxy = new ProgramList();
                mLegacyListProxy = new ProgramList();
                mLegacyListFilter = vendorFilter;
                mLegacyListFilter = vendorFilter;

                mCallback.clearLastCompleteList();
                mCallback.clearLastCompleteList();
                mCallback.setProgramListObserver(mLegacyListProxy, () -> { });
                mCallback.setProgramListObserver(mLegacyListProxy, () -> {
                    Log.i(TAG, "Empty closeListener in programListObserver");
                });
            }
        }
        try {
        try {
            mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
            mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
        } catch (RemoteException ex) {
        } catch (RemoteException ex) {
                    throw new RuntimeException("service died", ex);
            throw new RuntimeException("Service died", ex);
                }
        }
        }


        List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
        List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
            if (list == null) throw new IllegalStateException("Program list is not ready yet");
        if (list == null) {
            return list;
            throw new IllegalStateException("Program list is not ready yet");
        }
        }
        return list;
    }
    }


    @Override
    @Override
    public @Nullable ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
    @Nullable
        synchronized (mTuner) {
    public ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
        synchronized (mLock) {
            if (mLegacyListProxy != null) {
            if (mLegacyListProxy != null) {
                mLegacyListProxy.close();
                mLegacyListProxy.close();
                mLegacyListProxy = null;
                mLegacyListProxy = null;
            }
            }
            mLegacyListFilter = null;
            mLegacyListFilter = null;

        }
        ProgramList list = new ProgramList();
        ProgramList list = new ProgramList();
        mCallback.setProgramListObserver(list, () -> {
        mCallback.setProgramListObserver(list, () -> {
            try {
            try {
                mTuner.stopProgramListUpdates();
                mTuner.stopProgramListUpdates();
            } catch (IllegalStateException ex) {
            } catch (IllegalStateException ex) {
                // it's fine to not stop updates if tuner is already closed
                // it's fine to not stop updates if tuner is already closed
                Log.e(TAG, "Tuner may already be closed", ex);
            } catch (RemoteException ex) {
            } catch (RemoteException ex) {
                Log.e(TAG, "Couldn't stop program list updates", ex);
                Log.e(TAG, "Couldn't stop program list updates", ex);
            }
            }
@@ -284,13 +309,14 @@ class TunerAdapter extends RadioTuner {
            Log.i(TAG, "Program list is not supported with this hardware");
            Log.i(TAG, "Program list is not supported with this hardware");
            return null;
            return null;
        } catch (RemoteException ex) {
        } catch (RemoteException ex) {
                mCallback.setProgramListObserver(null, () -> { });
            mCallback.setProgramListObserver(null, () -> {
                throw new RuntimeException("service died", ex);
                Log.i(TAG, "Empty closeListener in programListObserver");
            });
            throw new RuntimeException("Service died", ex);
        }
        }


        return list;
        return list;
    }
    }
    }


    @Override
    @Override
    public boolean isAnalogForced() {
    public boolean isAnalogForced() {
@@ -315,7 +341,7 @@ class TunerAdapter extends RadioTuner {
        try {
        try {
            return mTuner.isConfigFlagSupported(flag);
            return mTuner.isConfigFlagSupported(flag);
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


@@ -324,7 +350,7 @@ class TunerAdapter extends RadioTuner {
        try {
        try {
            return mTuner.isConfigFlagSet(flag);
            return mTuner.isConfigFlagSet(flag);
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


@@ -333,25 +359,26 @@ class TunerAdapter extends RadioTuner {
        try {
        try {
            mTuner.setConfigFlag(flag, value);
            mTuner.setConfigFlag(flag, value);
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


    @Override
    @Override
    public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
    public Map<String, String> setParameters(Map<String, String> parameters) {
        try {
        try {
            return mTuner.setParameters(Objects.requireNonNull(parameters));
            return mTuner.setParameters(Objects.requireNonNull(parameters,
                    "Parameters cannot be null"));
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


    @Override
    @Override
    public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
    public Map<String, String> getParameters(List<String> keys) {
        try {
        try {
            return mTuner.getParameters(Objects.requireNonNull(keys));
            return mTuner.getParameters(Objects.requireNonNull(keys, "Keys cannot be null"));
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw new RuntimeException("service died", e);
            throw new RuntimeException("Service died", e);
        }
        }
    }
    }


+44 −18
Original line number Original line Diff line number Diff line
@@ -16,12 +16,13 @@


package android.hardware.radio;
package android.hardware.radio;


import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.util.Log;
import android.util.Log;


import com.android.internal.annotations.GuardedBy;

import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.Objects;
@@ -29,23 +30,31 @@ import java.util.Objects;
/**
/**
 * Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
 * Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
 */
 */
class TunerCallbackAdapter extends ITunerCallback.Stub {
final class TunerCallbackAdapter extends ITunerCallback.Stub {
    private static final String TAG = "BroadcastRadio.TunerCallbackAdapter";
    private static final String TAG = "BroadcastRadio.TunerCallbackAdapter";


    private final Object mLock = new Object();
    private final Object mLock = new Object();
    @NonNull private final RadioTuner.Callback mCallback;
    private final RadioTuner.Callback mCallback;
    @NonNull private final Handler mHandler;
    private final Handler mHandler;


    @GuardedBy("mLock")
    @Nullable ProgramList mProgramList;
    @Nullable ProgramList mProgramList;


    // cache for deprecated methods
    // cache for deprecated methods
    @GuardedBy("mLock")
    boolean mIsAntennaConnected = true;
    boolean mIsAntennaConnected = true;

    @GuardedBy("mLock")
    @Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
    @Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
    private boolean mDelayedCompleteCallback = false;

    @GuardedBy("mLock")
    private boolean mDelayedCompleteCallback;

    @GuardedBy("mLock")
    @Nullable RadioManager.ProgramInfo mCurrentProgramInfo;
    @Nullable RadioManager.ProgramInfo mCurrentProgramInfo;


    TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
    TunerCallbackAdapter(RadioTuner.Callback callback, @Nullable Handler handler) {
        mCallback = callback;
        mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
        if (handler == null) {
        if (handler == null) {
            mHandler = new Handler(Looper.getMainLooper());
            mHandler = new Handler(Looper.getMainLooper());
        } else {
        } else {
@@ -55,31 +64,39 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {


    void close() {
    void close() {
        synchronized (mLock) {
        synchronized (mLock) {
            if (mProgramList != null) mProgramList.close();
            if (mProgramList != null) {
                mProgramList.close();
            }
        }
        }
    }
    }


    void setProgramListObserver(@Nullable ProgramList programList,
    void setProgramListObserver(@Nullable ProgramList programList,
            @NonNull ProgramList.OnCloseListener closeListener) {
            ProgramList.OnCloseListener closeListener) {
        Objects.requireNonNull(closeListener);
        Objects.requireNonNull(closeListener, "CloseListener cannot be null");
        synchronized (mLock) {
        synchronized (mLock) {
            if (mProgramList != null) {
            if (mProgramList != null) {
                Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
                Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
                mProgramList.close();
                mProgramList.close();
            }
            }
            mProgramList = programList;
            mProgramList = programList;
            if (programList == null) return;
            if (programList == null) {
                return;
            }
            programList.setOnCloseListener(() -> {
            programList.setOnCloseListener(() -> {
                synchronized (mLock) {
                synchronized (mLock) {
                    if (mProgramList != programList) return;
                    if (mProgramList != programList) {
                        return;
                    }
                    mProgramList = null;
                    mProgramList = null;
                    mLastCompleteList = null;
                    mLastCompleteList = null;
                    closeListener.onClose();
                }
                }
                closeListener.onClose();
            });
            });
            programList.addOnCompleteListener(() -> {
            programList.addOnCompleteListener(() -> {
                synchronized (mLock) {
                synchronized (mLock) {
                    if (mProgramList != programList) return;
                    if (mProgramList != programList) {
                        return;
                    }
                    mLastCompleteList = programList.toList();
                    mLastCompleteList = programList.toList();
                    if (mDelayedCompleteCallback) {
                    if (mDelayedCompleteCallback) {
                        Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
                        Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
@@ -109,7 +126,11 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
    }
    }


    boolean isAntennaConnected() {
    boolean isAntennaConnected() {
        return mIsAntennaConnected;
        boolean isConnected;
        synchronized (mLock) {
            isConnected = mIsAntennaConnected;
        }
        return isConnected;
    }
    }


    @Override
    @Override
@@ -177,7 +198,9 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {


    @Override
    @Override
    public void onAntennaState(boolean connected) {
    public void onAntennaState(boolean connected) {
        synchronized (mLock) {
            mIsAntennaConnected = connected;
            mIsAntennaConnected = connected;
        }
        mHandler.post(() -> mCallback.onAntennaState(connected));
        mHandler.post(() -> mCallback.onAntennaState(connected));
    }
    }


@@ -186,6 +209,7 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
        mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
        mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
    }
    }


    @GuardedBy("mLock")
    private void sendBackgroundScanCompleteLocked() {
    private void sendBackgroundScanCompleteLocked() {
        mDelayedCompleteCallback = false;
        mDelayedCompleteCallback = false;
        mHandler.post(() -> mCallback.onBackgroundScanComplete());
        mHandler.post(() -> mCallback.onBackgroundScanComplete());
@@ -213,8 +237,10 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
    public void onProgramListUpdated(ProgramList.Chunk chunk) {
    public void onProgramListUpdated(ProgramList.Chunk chunk) {
        mHandler.post(() -> {
        mHandler.post(() -> {
            synchronized (mLock) {
            synchronized (mLock) {
                if (mProgramList == null) return;
                if (mProgramList == null) {
                mProgramList.apply(Objects.requireNonNull(chunk));
                    return;
                }
                mProgramList.apply(Objects.requireNonNull(chunk, "Chunk cannot be null"));
            }
            }
        });
        });
    }
    }