Loading core/java/android/hardware/radio/TunerCallbackAdapter.java +6 −4 Original line number Diff line number Diff line Loading @@ -211,10 +211,12 @@ class TunerCallbackAdapter extends ITunerCallback.Stub { @Override public void onProgramListUpdated(ProgramList.Chunk chunk) { mHandler.post(() -> { synchronized (mLock) { if (mProgramList == null) return; mProgramList.apply(Objects.requireNonNull(chunk)); } }); } @Override Loading core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java +10 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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); } } services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +32 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -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)); }); } }; Loading @@ -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) { Loading Loading @@ -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) { Loading Loading
core/java/android/hardware/radio/TunerCallbackAdapter.java +6 −4 Original line number Diff line number Diff line Loading @@ -211,10 +211,12 @@ class TunerCallbackAdapter extends ITunerCallback.Stub { @Override public void onProgramListUpdated(ProgramList.Chunk chunk) { mHandler.post(() -> { synchronized (mLock) { if (mProgramList == null) return; mProgramList.apply(Objects.requireNonNull(chunk)); } }); } @Override Loading
core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java +10 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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); } }
services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +32 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -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)); }); } }; Loading @@ -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) { Loading Loading @@ -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) { Loading