diff options
3 files changed, 48 insertions, 25 deletions
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java index 0fb93e532cd3..beff0f7607f8 100644 --- a/core/java/android/hardware/radio/TunerCallbackAdapter.java +++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java @@ -211,10 +211,12 @@ class TunerCallbackAdapter extends ITunerCallback.Stub { @Override public void onProgramListUpdated(ProgramList.Chunk chunk) { - synchronized (mLock) { - if (mProgramList == null) return; - mProgramList.apply(Objects.requireNonNull(chunk)); - } + mHandler.post(() -> { + synchronized (mLock) { + if (mProgramList == null) return; + mProgramList.apply(Objects.requireNonNull(chunk)); + } + }); } @Override diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java index 3d9a1d9398c5..7c6271cbdf61 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,6 +44,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import org.mockito.verification.VerificationWithTimeout; import java.util.Arrays; import java.util.HashSet; @@ -56,6 +58,8 @@ import java.util.List; public class StartProgramListUpdatesFanoutTest { private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout"; + private static final VerificationWithTimeout CB_TIMEOUT = timeout(100); + // Mocks @Mock IBroadcastRadio mBroadcastRadioMock; @Mock ITunerSession mHalTunerSessionMock; @@ -200,10 +204,10 @@ public class StartProgramListUpdatesFanoutTest { // Adding mDabEnsembleInfo should not update any client. updateHalProgramInfo(false, Arrays.asList(mDabEnsembleInfo), null); - verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any()); - verify(mAidlTunerCallbackMocks[1], times(2)).onProgramListUpdated(any()); - verify(mAidlTunerCallbackMocks[2], times(1)).onProgramListUpdated(any()); - verify(mAidlTunerCallbackMocks[3], times(2)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[0], CB_TIMEOUT.times(1)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[1], CB_TIMEOUT.times(2)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[2], CB_TIMEOUT.times(1)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[3], CB_TIMEOUT.times(2)).onProgramListUpdated(any()); } @Test @@ -240,7 +244,7 @@ public class StartProgramListUpdatesFanoutTest { // Update the HAL with mModifiedAmFmInfo, and verify only the remaining client is updated. updateHalProgramInfo(true, Arrays.asList(mModifiedAmFmInfo), null); - verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[0], CB_TIMEOUT.times(1)).onProgramListUpdated(any()); verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], true, Arrays.asList(mModifiedAmFmInfo), null); @@ -313,6 +317,6 @@ public class StartProgramListUpdatesFanoutTest { } ProgramList.Chunk expectedChunk = new ProgramList.Chunk(purge, true, modifiedSet, removedSet); - verify(clientMock).onProgramListUpdated(expectedChunk); + verify(clientMock, CB_TIMEOUT).onProgramListUpdated(expectedChunk); } } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index 53890a48a674..a0eafb4fc93f 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -35,6 +35,8 @@ import android.hardware.broadcastradio.V2_0.Result; import android.hardware.broadcastradio.V2_0.VendorKeyValue; import android.hardware.radio.RadioManager; import android.os.DeadObjectException; +import android.os.Handler; +import android.os.Looper; import android.os.RemoteException; import android.util.MutableInt; import android.util.Slog; @@ -45,6 +47,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -56,6 +59,7 @@ class RadioModule { @NonNull public final RadioManager.ModuleProperties mProperties; private final Object mLock = new Object(); + @NonNull private final Handler mHandler; @GuardedBy("mLock") private ITunerSession mHalTunerSession; @@ -77,22 +81,24 @@ class RadioModule { private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() { @Override public void onTuneFailed(int result, ProgramSelector programSelector) { - fanoutAidlCallback(cb -> cb.onTuneFailed(result, Convert.programSelectorFromHal( - programSelector))); + lockAndFireLater(() -> { + android.hardware.radio.ProgramSelector csel = + Convert.programSelectorFromHal(programSelector); + fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel)); + }); } @Override public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) { - RadioManager.ProgramInfo programInfo = Convert.programInfoFromHal(halProgramInfo); - synchronized (mLock) { - mCurrentProgramInfo = programInfo; - fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(programInfo)); - } + lockAndFireLater(() -> { + mCurrentProgramInfo = Convert.programInfoFromHal(halProgramInfo); + fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(mCurrentProgramInfo)); + }); } @Override public void onProgramListUpdated(ProgramListChunk programListChunk) { - synchronized (mLock) { + lockAndFireLater(() -> { android.hardware.radio.ProgramList.Chunk chunk = Convert.programListChunkFromHal(programListChunk); mProgramInfoCache.filterAndApplyChunk(chunk); @@ -100,20 +106,23 @@ class RadioModule { for (TunerSession tunerSession : mAidlTunerSessions) { tunerSession.onMergedProgramListUpdateFromHal(chunk); } - } + }); } @Override public void onAntennaStateChange(boolean connected) { - synchronized (mLock) { + lockAndFireLater(() -> { mAntennaConnected = connected; fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected)); - } + }); } @Override public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) { - fanoutAidlCallback(cb -> cb.onParametersUpdated(Convert.vendorInfoFromHal(parameters))); + lockAndFireLater(() -> { + Map<String, String> cparam = Convert.vendorInfoFromHal(parameters); + fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam)); + }); } }; @@ -126,6 +135,7 @@ class RadioModule { @NonNull RadioManager.ModuleProperties properties) { mProperties = Objects.requireNonNull(properties); mService = Objects.requireNonNull(service); + mHandler = new Handler(Looper.getMainLooper()); } public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) { @@ -310,15 +320,22 @@ class RadioModule { } } + // add to mHandler queue, but ensure the runnable holds mLock when it gets executed + private void lockAndFireLater(Runnable r) { + mHandler.post(() -> { + synchronized (mLock) { + r.run(); + } + }); + } + interface AidlCallbackRunnable { void run(android.hardware.radio.ITunerCallback callback) throws RemoteException; } // Invokes runnable with each TunerSession currently open. void fanoutAidlCallback(AidlCallbackRunnable runnable) { - synchronized (mLock) { - fanoutAidlCallbackLocked(runnable); - } + lockAndFireLater(() -> fanoutAidlCallbackLocked(runnable)); } private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) { |