From c0b7cdc45971590096cc7a16352a431b361ee755 Mon Sep 17 00:00:00 2001 From: Weilin Xu Date: Wed, 7 Dec 2022 00:21:40 +0000 Subject: [PATCH] Add SDK version checking in AIDL radio HAL client The target SDK version checking was added in AIDL broadcast radio HAL to guarantee that broadcast radio HAL can pass object including new HAL features to the application only when the application has a target SDK version at least the SDK version with the new features. If the application SDK version is lower, the program selectors and program infos will not be sent back to applications if they contain features only existing in higher SDK version, and items only existing in higher version will be removed from the chunk before sending back to applications. HIDL 1.x and 2.0 HAL clients were not updated with SDK version since new broadcast radio HAL features are no longer landed in them. Bug: 257337458 Test: atest android.hardware.radio.tests.unittests Test: atest com.android.server.broadcastradio Change-Id: Ia716353d9c796c5dc5f5218d33fd20a129a5370a --- .../android/hardware/radio/IRadioService.aidl | 2 +- .../android/hardware/radio/RadioManager.java | 4 +- .../tests/unittests/ProgramListTest.java | 10 ++-- .../tests/unittests/RadioManagerTest.java | 15 ++++- .../tests/unittests/TunerAdapterTest.java | 9 ++- .../IRadioServiceAidlImplTest.java | 7 ++- .../IRadioServiceHidlImplTest.java | 6 +- .../aidl/BroadcastRadioServiceImplTest.java | 14 +++-- .../aidl/ConversionUtilsTest.java | 13 ++++ .../broadcastradio/aidl/TunerSessionTest.java | 22 ++++++- .../broadcastradio/IRadioServiceAidlImpl.java | 5 +- .../broadcastradio/IRadioServiceHidlImpl.java | 3 +- .../aidl/BroadcastRadioServiceImpl.java | 5 +- .../broadcastradio/aidl/ConversionUtils.java | 60 +++++++++++++++++++ .../broadcastradio/aidl/RadioModule.java | 38 +++++++++--- .../broadcastradio/aidl/TunerSession.java | 24 ++++++-- .../hal2/BroadcastRadioService.java | 2 +- 17 files changed, 199 insertions(+), 40 deletions(-) diff --git a/core/java/android/hardware/radio/IRadioService.aidl b/core/java/android/hardware/radio/IRadioService.aidl index 9349cf7d0c327..c7131a7fe9c95 100644 --- a/core/java/android/hardware/radio/IRadioService.aidl +++ b/core/java/android/hardware/radio/IRadioService.aidl @@ -31,7 +31,7 @@ interface IRadioService { List listModules(); ITuner openTuner(int moduleId, in RadioManager.BandConfig bandConfig, boolean withAudio, - in ITunerCallback callback); + in ITunerCallback callback, int targetSdkVersion); ICloseHandle addAnnouncementListener(in int[] enabledTypes, in IAnnouncementListener listener); diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 8c6083ce49b63..f072e3b43b4ad 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -1796,7 +1796,7 @@ public class RadioManager { ITuner tuner; TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler); try { - tuner = mService.openTuner(moduleId, config, withAudio, halCallback); + tuner = mService.openTuner(moduleId, config, withAudio, halCallback, mTargetSdkVersion); } catch (RemoteException | IllegalArgumentException | IllegalStateException ex) { Log.e(TAG, "Failed to open tuner", ex); return null; @@ -1873,6 +1873,7 @@ public class RadioManager { @NonNull private final Context mContext; @NonNull private final IRadioService mService; + private final int mTargetSdkVersion; /** * @hide @@ -1889,5 +1890,6 @@ public class RadioManager { public RadioManager(Context context, IRadioService service) { mContext = context; mService = service; + mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; } } diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java index 2b9de18701e2e..87f91fa436ed3 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java @@ -41,6 +41,8 @@ import android.os.Parcel; import android.os.RemoteException; import android.util.ArraySet; +import androidx.test.InstrumentationRegistry; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -54,6 +56,8 @@ import java.util.concurrent.Executor; @RunWith(MockitoJUnitRunner.class) public final class ProgramListTest { + public final Context mContext = InstrumentationRegistry.getContext(); + private static final int CREATOR_ARRAY_SIZE = 3; private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(/* millis= */ 500); @@ -109,8 +113,6 @@ public final class ProgramListTest { @Mock private IRadioService mRadioServiceMock; @Mock - private Context mContextMock; - @Mock private ITuner mTunerMock; @Mock private RadioTuner.Callback mTunerCallbackMock; @@ -477,7 +479,7 @@ public final class ProgramListTest { } private void createRadioTuner() throws Exception { - RadioManager radioManager = new RadioManager(mContextMock, mRadioServiceMock); + RadioManager radioManager = new RadioManager(mContext, mRadioServiceMock); RadioManager.BandConfig band = new RadioManager.FmBandConfig( new RadioManager.FmBandDescriptor(RadioManager.REGION_ITU_1, RadioManager.BAND_FM, /* lowerLimit= */ 87500, /* upperLimit= */ 108000, /* spacing= */ 200, @@ -487,7 +489,7 @@ public final class ProgramListTest { doAnswer(invocation -> { mTunerCallback = (ITunerCallback) invocation.getArguments()[3]; return mTunerMock; - }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any()); + }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any(), anyInt()); mRadioTuner = radioManager.openTuner(/* moduleId= */ 0, band, /* withAudio= */ true, mTunerCallbackMock, /* handler= */ null); diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java index 44aa6d19cb0a2..03742eb93a3bb 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java @@ -18,14 +18,16 @@ package android.hardware.radio.tests.unittests; import static com.google.common.truth.Truth.assertWithMessage; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.hardware.radio.Announcement; import android.hardware.radio.IAnnouncementListener; import android.hardware.radio.ICloseHandle; @@ -34,6 +36,7 @@ import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioMetadata; import android.hardware.radio.RadioTuner; +import android.os.Build; import android.os.Parcel; import android.os.RemoteException; import android.util.ArrayMap; @@ -53,6 +56,8 @@ import java.util.Set; @RunWith(MockitoJUnitRunner.class) public final class RadioManagerTest { + private static final int TEST_TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; + private static final int REGION = RadioManager.REGION_ITU_2; private static final int FM_LOWER_LIMIT = 87500; private static final int FM_UPPER_LIMIT = 108000; @@ -126,6 +131,7 @@ public final class RadioManagerTest { /* vendorInfo= */ new ArrayMap<>())); private RadioManager mRadioManager; + private final ApplicationInfo mApplicationInfo = new ApplicationInfo(); @Mock private IRadioService mRadioServiceMock; @@ -1008,7 +1014,8 @@ public final class RadioManagerTest { mRadioManager.openTuner(moduleId, FM_BAND_CONFIG, withAudio, mCallbackMock, /* handler= */ null); - verify(mRadioServiceMock).openTuner(eq(moduleId), eq(FM_BAND_CONFIG), eq(withAudio), any()); + verify(mRadioServiceMock).openTuner(eq(moduleId), eq(FM_BAND_CONFIG), eq(withAudio), any(), + anyInt()); } @Test @@ -1103,6 +1110,8 @@ public final class RadioManagerTest { } private void createRadioManager() throws RemoteException { + mApplicationInfo.targetSdkVersion = TEST_TARGET_SDK_VERSION; + when(mContextMock.getApplicationInfo()).thenReturn(mApplicationInfo); when(mRadioServiceMock.listModules()).thenReturn(Arrays.asList(AMFM_PROPERTIES)); when(mRadioServiceMock.addAnnouncementListener(any(), any())).thenReturn(mCloseHandleMock); diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java index 6d77f99361964..7a9976779df9f 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.graphics.Bitmap; import android.hardware.radio.IRadioService; import android.hardware.radio.ITuner; @@ -35,6 +36,7 @@ import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioMetadata; import android.hardware.radio.RadioTuner; +import android.os.Build; import org.junit.After; import org.junit.Before; @@ -51,6 +53,8 @@ import java.util.Map; @RunWith(MockitoJUnitRunner.class) public final class TunerAdapterTest { + private static final int TEST_TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; + private static final int CALLBACK_TIMEOUT_MS = 30_000; private static final int AM_LOWER_LIMIT_KHZ = 150; @@ -65,6 +69,7 @@ public final class TunerAdapterTest { private RadioTuner mRadioTuner; private ITunerCallback mTunerCallback; + private final ApplicationInfo mApplicationInfo = new ApplicationInfo(); @Mock private IRadioService mRadioServiceMock; @@ -77,12 +82,14 @@ public final class TunerAdapterTest { @Before public void setUp() throws Exception { + mApplicationInfo.targetSdkVersion = TEST_TARGET_SDK_VERSION; + when(mContextMock.getApplicationInfo()).thenReturn(mApplicationInfo); RadioManager radioManager = new RadioManager(mContextMock, mRadioServiceMock); doAnswer(invocation -> { mTunerCallback = (ITunerCallback) invocation.getArguments()[3]; return mTunerMock; - }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any()); + }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any(), anyInt()); doAnswer(invocation -> { ProgramSelector program = (ProgramSelector) invocation.getArguments()[0]; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java index a2df426c315c8..9803474566fbf 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,6 +34,7 @@ import android.hardware.radio.ICloseHandle; import android.hardware.radio.ITuner; import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; +import android.os.Build; import android.os.IBinder; import android.os.ServiceManager; @@ -55,6 +57,7 @@ public final class IRadioServiceAidlImplTest extends ExtendedRadioMockitoTestCas "android.hardware.broadcastradio.IBroadcastRadio/amfm"; private static final String DAB_SERVICE_NAME = "android.hardware.broadcastradio.IBroadcastRadio/dab"; + private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; private IRadioServiceAidlImpl mAidlImpl; @@ -82,7 +85,7 @@ public final class IRadioServiceAidlImplTest extends ExtendedRadioMockitoTestCas doNothing().when(mServiceMock).enforcePolicyAccess(); when(mHalMock.listModules()).thenReturn(List.of(mModuleMock)); - when(mHalMock.openSession(anyInt(), any(), anyBoolean(), any())) + when(mHalMock.openSession(anyInt(), any(), anyBoolean(), any(), eq(TARGET_SDK_VERSION))) .thenReturn(mTunerMock); when(mHalMock.addAnnouncementListener(any(), any())).thenReturn(mICloseHandle); @@ -114,7 +117,7 @@ public final class IRadioServiceAidlImplTest extends ExtendedRadioMockitoTestCas @Test public void openTuner_forAidlImpl() throws Exception { ITuner tuner = mAidlImpl.openTuner(/* moduleId= */ 0, mBandConfigMock, - /* withAudio= */ true, mTunerCallbackMock); + /* withAudio= */ true, mTunerCallbackMock, TARGET_SDK_VERSION); assertWithMessage("Tuner opened in AIDL HAL") .that(tuner).isEqualTo(mTunerMock); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java index 5ab943542f81d..cfff477362400 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java @@ -32,6 +32,7 @@ import android.hardware.radio.ICloseHandle; import android.hardware.radio.ITuner; import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; +import android.os.Build; import org.junit.Before; import org.junit.Test; @@ -49,6 +50,7 @@ public final class IRadioServiceHidlImplTest { private static final int HAL1_MODULE_ID = 0; private static final int[] ENABLE_TYPES = new int[]{Announcement.TYPE_TRAFFIC}; + private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; private IRadioServiceHidlImpl mHidlImpl; @@ -103,7 +105,7 @@ public final class IRadioServiceHidlImplTest { @Test public void openTuner_withHal1ModuleId_forHidlImpl() throws Exception { ITuner tuner = mHidlImpl.openTuner(HAL1_MODULE_ID, mBandConfigMock, - /* withAudio= */ true, mTunerCallbackMock); + /* withAudio= */ true, mTunerCallbackMock, TARGET_SDK_VERSION); assertWithMessage("Tuner opened in HAL 1") .that(tuner).isEqualTo(mHal1TunerMock); @@ -112,7 +114,7 @@ public final class IRadioServiceHidlImplTest { @Test public void openTuner_withHal2ModuleId_forHidlImpl() throws Exception { ITuner tuner = mHidlImpl.openTuner(HAL1_MODULE_ID + 1, mBandConfigMock, - /* withAudio= */ true, mTunerCallbackMock); + /* withAudio= */ true, mTunerCallbackMock, TARGET_SDK_VERSION); assertWithMessage("Tuner opened in HAL 2") .that(tuner).isEqualTo(mHal2TunerMock); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java index 1cc0a98512bf5..f4040825e9a74 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java @@ -36,6 +36,7 @@ import android.hardware.radio.ITuner; import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioTuner; +import android.os.Build; import android.os.IBinder; import android.os.IServiceCallback; import android.os.RemoteException; @@ -54,6 +55,8 @@ import java.util.Arrays; public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTestCase { + private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; + private static final int FM_RADIO_MODULE_ID = 0; private static final int DAB_RADIO_MODULE_ID = 1; private static final ArrayList SERVICE_LIST = @@ -137,7 +140,8 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes createBroadcastRadioService(); ITuner session = mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID, - /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock); + /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock, + TARGET_SDK_VERSION); assertWithMessage("Session opened in FM radio module") .that(session).isEqualTo(mFmTunerSessionMock); @@ -148,7 +152,8 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes createBroadcastRadioService(); ITuner session = mBroadcastRadioService.openSession(DAB_RADIO_MODULE_ID + 1, - /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock); + /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock, + TARGET_SDK_VERSION); assertWithMessage("Session opened with id not found").that(session).isNull(); } @@ -160,7 +165,8 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID, - /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock)); + /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock, + TARGET_SDK_VERSION)); assertWithMessage("Exception for opening session by non-current user") .that(thrown).hasMessageThat().contains("Cannot open session for non-current user"); @@ -228,7 +234,7 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes return null; }).when(mFmBinderMock).linkToDeath(any(), anyInt()); - when(mFmRadioModuleMock.openSession(eq(mTunerCallbackMock))) + when(mFmRadioModuleMock.openSession(eq(mTunerCallbackMock), eq(TARGET_SDK_VERSION))) .thenReturn(mFmTunerSessionMock); } } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java index 31195548c9685..a1cebb630aac3 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java @@ -25,6 +25,7 @@ import android.hardware.broadcastradio.VendorKeyValue; import android.hardware.radio.Announcement; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; +import android.os.Build; import com.google.common.truth.Expect; @@ -68,6 +69,18 @@ public final class ConversionUtilsTest { @Rule public final Expect expect = Expect.create(); + @Test + public void isAtLeastU_withTSdkVersion_returnsFalse() { + expect.withMessage("Target SDK version of T") + .that(ConversionUtils.isAtLeastU(Build.VERSION_CODES.TIRAMISU)).isFalse(); + } + + @Test + public void isAtLeastU_withCurrentSdkVersion_returnsTrue() { + expect.withMessage("Target SDK version of U") + .that(ConversionUtils.isAtLeastU(Build.VERSION_CODES.CUR_DEVELOPMENT)).isTrue(); + } + @Test public void propertiesFromHalProperties_idsMatch() { expect.withMessage("Properties id") diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index b76171aef4663..36c96260ae8f2 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -42,6 +42,7 @@ import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioTuner; +import android.os.Build; import android.os.ServiceSpecificException; import android.util.ArrayMap; import android.util.ArraySet; @@ -65,6 +66,7 @@ import java.util.Map; */ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { + private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(/* millis= */ 200); private static final int SIGNAL_QUALITY = 1; @@ -298,6 +300,18 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); } + @Test + public void tune_withLowerSdkVersion() throws Exception { + openAidlClients(/* numClients= */ 1, Build.VERSION_CODES.TIRAMISU); + ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + RadioManager.ProgramInfo tuneInfo = + AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); + + mTunerSessions[0].tune(initialSel); + + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + } + @Test public void tune_withMultipleSessions() throws Exception { int numSessions = 3; @@ -627,13 +641,17 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { .onParametersUpdated(parametersExpected); } } - private void openAidlClients(int numClients) throws Exception { + openAidlClients(numClients, TARGET_SDK_VERSION); + } + + private void openAidlClients(int numClients, int targetSdkVersion) throws Exception { mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; mTunerSessions = new TunerSession[numClients]; for (int index = 0; index < numClients; index++) { mAidlTunerCallbackMocks[index] = mock(android.hardware.radio.ITunerCallback.class); - mTunerSessions[index] = mRadioModule.openSession(mAidlTunerCallbackMocks[index]); + mTunerSessions[index] = mRadioModule.openSession(mAidlTunerCallbackMocks[index], + targetSdkVersion); } } diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java index 6a010424db139..b66120d428c3f 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java @@ -86,7 +86,8 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { @Override public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig, - boolean withAudio, ITunerCallback callback) throws RemoteException { + boolean withAudio, ITunerCallback callback, int targetSdkVersion) + throws RemoteException { if (isDebugEnabled()) { Slogf.d(TAG, "Opening module %d", moduleId); } @@ -94,7 +95,7 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { if (callback == null) { throw new IllegalArgumentException("Callback must not be null"); } - return mHalAidl.openSession(moduleId, bandConfig, withAudio, callback); + return mHalAidl.openSession(moduleId, bandConfig, withAudio, callback, targetSdkVersion); } @Override diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java index 408fba1bff3b1..8a1ba19e24ab7 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java @@ -92,7 +92,8 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub { @Override public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig, - boolean withAudio, ITunerCallback callback) throws RemoteException { + boolean withAudio, ITunerCallback callback, int targetSdkVersion) + throws RemoteException { if (isDebugEnabled()) { Slog.d(TAG, "Opening module " + moduleId); } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java index 03acf72725e7d..772cd4144f11b 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java @@ -199,7 +199,8 @@ public final class BroadcastRadioServiceImpl { */ @Nullable public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig, - boolean withAudio, ITunerCallback callback) throws RemoteException { + boolean withAudio, ITunerCallback callback, int targetSdkVersion) + throws RemoteException { if (DEBUG) { Slogf.d(TAG, "Open AIDL radio session"); } @@ -222,7 +223,7 @@ public final class BroadcastRadioServiceImpl { } } - TunerSession tunerSession = radioModule.openSession(callback); + TunerSession tunerSession = radioModule.openSession(callback, targetSdkVersion); if (legacyConfig != null) { tunerSession.setConfiguration(legacyConfig); } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java index 09ee99659afca..9a7a3b5e18706 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java @@ -33,6 +33,7 @@ import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioMetadata; +import android.os.Build; import android.os.ParcelableException; import android.os.ServiceSpecificException; import android.util.ArrayMap; @@ -62,6 +63,11 @@ final class ConversionUtils { throw new UnsupportedOperationException("ConversionUtils class is noninstantiable"); } + static boolean isAtLeastU(int targetSdkVersion) { + // TODO(b/261770108): Use version code for U. + return targetSdkVersion >= Build.VERSION_CODES.CUR_DEVELOPMENT; + } + static RuntimeException throwOnError(RuntimeException halException, String action) { if (!(halException instanceof ServiceSpecificException)) { return new ParcelableException(new RuntimeException( @@ -472,6 +478,60 @@ final class ConversionUtils { return new ProgramList.Chunk(chunk.purge, chunk.complete, modified, removed); } + private static boolean isNewIdentifierInU(ProgramSelector.Identifier id) { + return id.getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT; + } + + static boolean programSelectorMeetsSdkVersionRequirement(ProgramSelector sel, + int targetSdkVersion) { + if (isAtLeastU(targetSdkVersion)) { + return true; + } + if (sel.getPrimaryId().getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) { + return false; + } + ProgramSelector.Identifier[] secondaryIds = sel.getSecondaryIds(); + for (int i = 0; i < secondaryIds.length; i++) { + if (isNewIdentifierInU(secondaryIds[i])) { + return false; + } + } + return true; + } + + static boolean programInfoMeetsSdkVersionRequirement(RadioManager.ProgramInfo info, + int targetSdkVersion) { + if (isAtLeastU(targetSdkVersion)) { + return true; + } + if (!programSelectorMeetsSdkVersionRequirement(info.getSelector(), targetSdkVersion)) { + return false; + } + if (isNewIdentifierInU(info.getLogicallyTunedTo()) + || isNewIdentifierInU(info.getPhysicallyTunedTo())) { + return false; + } + Iterator relatedContentIt = info.getRelatedContent().iterator(); + while (relatedContentIt.hasNext()) { + if (isNewIdentifierInU(relatedContentIt.next())) { + return false; + } + } + return true; + } + + static ProgramList.Chunk convertChunkToTargetSdkVersion(ProgramList.Chunk chunk, + int targetSdkVersion) { + if (isAtLeastU(targetSdkVersion)) { + return chunk; + } + Set modified = chunk.getModified(); + modified.removeIf(info -> !programInfoMeetsSdkVersionRequirement(info, targetSdkVersion)); + Set removed = chunk.getRemoved(); + removed.removeIf(id -> isNewIdentifierInU(id)); + return new ProgramList.Chunk(chunk.isPurge(), chunk.isComplete(), modified, removed); + } + public static android.hardware.radio.Announcement announcementFromHalAnnouncement( Announcement hwAnnouncement) { return new android.hardware.radio.Announcement( diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java index 5a3bd28cb7047..47203c8854a3a 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java @@ -100,7 +100,15 @@ final class RadioModule { synchronized (mLock) { android.hardware.radio.ProgramSelector csel = ConversionUtils.programSelectorFromHalProgramSelector(programSelector); - fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel)); + fanoutAidlCallbackLocked((cb, sdkVersion) -> { + if (csel != null && !ConversionUtils + .programSelectorMeetsSdkVersionRequirement(csel, sdkVersion)) { + Slogf.e(TAG, "onTuneFailed: cannot send program selector " + + "requiring higher target SDK version"); + return; + } + cb.onTuneFailed(result, csel); + }); } }); } @@ -112,7 +120,13 @@ final class RadioModule { mCurrentProgramInfo = ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo); RadioManager.ProgramInfo currentProgramInfo = mCurrentProgramInfo; - fanoutAidlCallbackLocked(cb -> { + fanoutAidlCallbackLocked((cb, sdkVersion) -> { + if (!ConversionUtils.programInfoMeetsSdkVersionRequirement( + currentProgramInfo, sdkVersion)) { + Slogf.e(TAG, "onCurrentProgramInfoChanged: cannot send " + + "program info requiring higher target SDK version"); + return; + } cb.onCurrentProgramInfoChanged(currentProgramInfo); }); } @@ -139,7 +153,7 @@ final class RadioModule { fireLater(() -> { synchronized (mLock) { mAntennaConnected = connected; - fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected)); + fanoutAidlCallbackLocked((cb, sdkVersion) -> cb.onAntennaState(connected)); } }); } @@ -148,7 +162,9 @@ final class RadioModule { public void onConfigFlagUpdated(int flag, boolean value) { fireLater(() -> { synchronized (mLock) { - fanoutAidlCallbackLocked(cb -> cb.onConfigFlagUpdated(flag, value)); + fanoutAidlCallbackLocked((cb, sdkVersion) -> { + cb.onConfigFlagUpdated(flag, value); + }); } }); } @@ -159,7 +175,9 @@ final class RadioModule { synchronized (mLock) { Map cparam = ConversionUtils.vendorInfoFromHalVendorKeyValues(parameters); - fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam)); + fanoutAidlCallbackLocked((cb, sdkVersion) -> { + cb.onParametersUpdated(cparam); + }); } }); } @@ -223,14 +241,14 @@ final class RadioModule { mService.setTunerCallback(mHalTunerCallback); } - TunerSession openSession(android.hardware.radio.ITunerCallback userCb) + TunerSession openSession(android.hardware.radio.ITunerCallback userCb, int targetSdkVersion) throws RemoteException { mLogger.logRadioEvent("Open TunerSession"); TunerSession tunerSession; Boolean antennaConnected; RadioManager.ProgramInfo currentProgramInfo; synchronized (mLock) { - tunerSession = new TunerSession(this, mService, userCb); + tunerSession = new TunerSession(this, mService, userCb, targetSdkVersion); mAidlTunerSessions.add(tunerSession); antennaConnected = mAntennaConnected; currentProgramInfo = mCurrentProgramInfo; @@ -383,7 +401,8 @@ final class RadioModule { } interface AidlCallbackRunnable { - void run(android.hardware.radio.ITunerCallback callback) throws RemoteException; + void run(android.hardware.radio.ITunerCallback callback, int targetSdkVersion) + throws RemoteException; } // Invokes runnable with each TunerSession currently open. @@ -400,7 +419,8 @@ final class RadioModule { List deadSessions = null; for (int i = 0; i < mAidlTunerSessions.size(); i++) { try { - runnable.run(mAidlTunerSessions.valueAt(i).mCallback); + runnable.run(mAidlTunerSessions.valueAt(i).mCallback, + mAidlTunerSessions.valueAt(i).getTargetSdkVersion()); } catch (DeadObjectException ex) { // The other side died without calling close(), so just purge it from our records. Slogf.e(TAG, "Removing dead TunerSession"); diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java index 66e1be183a0f2..d700ed01b3c35 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java @@ -21,7 +21,6 @@ import android.graphics.Bitmap; import android.hardware.broadcastradio.ConfigFlag; import android.hardware.broadcastradio.IBroadcastRadio; import android.hardware.radio.ITuner; -import android.hardware.radio.ITunerCallback; import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; @@ -47,6 +46,7 @@ final class TunerSession extends ITuner.Stub { private final RadioLogger mLogger; private final RadioModule mModule; final android.hardware.radio.ITunerCallback mCallback; + private final int mTargetSdkVersion; private final IBroadcastRadio mService; @GuardedBy("mLock") @@ -61,10 +61,11 @@ final class TunerSession extends ITuner.Stub { private RadioManager.BandConfig mPlaceHolderConfig; TunerSession(RadioModule radioModule, IBroadcastRadio service, - android.hardware.radio.ITunerCallback callback) { + android.hardware.radio.ITunerCallback callback, int targetSdkVersion) { mModule = Objects.requireNonNull(radioModule, "radioModule cannot be null"); mService = Objects.requireNonNull(service, "service cannot be null"); mCallback = Objects.requireNonNull(callback, "callback cannot be null"); + mTargetSdkVersion = targetSdkVersion; mLogger = new RadioLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE); } @@ -129,7 +130,7 @@ final class TunerSession extends ITuner.Stub { mPlaceHolderConfig = Objects.requireNonNull(config, "config cannot be null"); } Slogf.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL AIDL"); - mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config)); + mModule.fanoutAidlCallback((cb, sdkVersion) -> cb.onConfigurationChanged(config)); } @Override @@ -248,7 +249,9 @@ final class TunerSession extends ITuner.Stub { Slogf.w(TAG, "Cannot start background scan on AIDL HAL client from non-current user"); return false; } - mModule.fanoutAidlCallback(ITunerCallback::onBackgroundScanComplete); + mModule.fanoutAidlCallback((cb, sdkVersion) -> { + cb.onBackgroundScanComplete(); + }); return true; } @@ -276,6 +279,10 @@ final class TunerSession extends ITuner.Stub { mModule.onTunerSessionProgramListFilterChanged(this); } + int getTargetSdkVersion() { + return mTargetSdkVersion; + } + ProgramList.Filter getProgramListFilter() { synchronized (mLock) { return mProgramInfoCache == null ? null : mProgramInfoCache.getFilter(); @@ -311,7 +318,14 @@ final class TunerSession extends ITuner.Stub { } for (int i = 0; i < chunks.size(); i++) { try { - mCallback.onProgramListUpdated(chunks.get(i)); + if (!ConversionUtils.isAtLeastU(getTargetSdkVersion())) { + ProgramList.Chunk downgradedChunk = + ConversionUtils.convertChunkToTargetSdkVersion(chunks.get(i), + getTargetSdkVersion()); + mCallback.onProgramListUpdated(downgradedChunk); + } else { + mCallback.onProgramListUpdated(chunks.get(i)); + } } catch (RemoteException ex) { Slogf.w(TAG, ex, "mCallback.onProgramListUpdated() failed"); } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java index 984bf5125582e..1e31f200fd47d 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java @@ -169,7 +169,7 @@ public final class BroadcastRadioService { } public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig, - boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException { + boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException { Slog.v(TAG, "Open HIDL 2.0 session with module id " + moduleId); if (!RadioServiceUserController.isCurrentOrSystemUser()) { Slogf.e(TAG, "Cannot open tuner on HAL 2.0 client for non-current user");