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
This commit is contained in:
@@ -31,7 +31,7 @@ interface IRadioService {
|
||||
List<RadioManager.ModuleProperties> 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<String> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<ProgramSelector.Identifier> 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<RadioManager.ProgramInfo> modified = chunk.getModified();
|
||||
modified.removeIf(info -> !programInfoMeetsSdkVersionRequirement(info, targetSdkVersion));
|
||||
Set<ProgramSelector.Identifier> 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(
|
||||
|
||||
@@ -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<String, String> 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<TunerSession> 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");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user