Initial implementation of Java-based broadcast radio service.

It provides only limited amount of service, without actual interaction
with HAL.

Added config.enable_java_radio switch to use Java-based service instead
of native. Added FEATURE_RADIO to PackageManager.

Bug: b/36863239
Test: Instrumentation, manual (Kitchen Sink)

Change-Id: I01139d326893c0a437c60cc35d6e5b005da35231
This commit is contained in:
Tomasz Wasilczyk
2017-04-04 11:13:44 -07:00
parent 9ecd024245
commit 347192e0da
13 changed files with 484 additions and 8 deletions

View File

@@ -216,6 +216,8 @@ LOCAL_SRC_FILES += \
core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
core/java/android/hardware/location/IContextHubCallback.aidl \
core/java/android/hardware/location/IContextHubService.aidl \
core/java/android/hardware/radio/IRadioService.aidl \
core/java/android/hardware/radio/ITuner.aidl \
core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/ICaptivePortal.aidl \
@@ -674,6 +676,7 @@ aidl_files := \
frameworks/base/core/java/android/print/PrinterInfo.aidl \
frameworks/base/core/java/android/print/PrintJobId.aidl \
frameworks/base/core/java/android/printservice/recommendation/RecommendationInfo.aidl \
frameworks/base/core/java/android/hardware/radio/RadioManager.aidl \
frameworks/base/core/java/android/hardware/usb/UsbDevice.aidl \
frameworks/base/core/java/android/hardware/usb/UsbInterface.aidl \
frameworks/base/core/java/android/hardware/usb/UsbEndpoint.aidl \

View File

@@ -784,7 +784,7 @@ final class SystemServiceRegistry {
registerService(Context.RADIO_SERVICE, RadioManager.class,
new CachedServiceFetcher<RadioManager>() {
@Override
public RadioManager createService(ContextImpl ctx) {
public RadioManager createService(ContextImpl ctx) throws ServiceNotFoundException {
return new RadioManager(ctx);
}});

View File

@@ -1828,6 +1828,14 @@ public abstract class PackageManager {
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
/**
* The device includes broadcast radio tuner.
*
* @hide FutureFeature
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_RADIO = "android.hardware.radio";
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device includes an accelerometer.

View File

@@ -0,0 +1,28 @@
/**
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.radio;
import android.hardware.radio.ITuner;
/**
* API to the broadcast radio service.
*
* {@hide}
*/
interface IRadioService {
ITuner openTuner();
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.radio;
import android.hardware.radio.RadioManager;
/** {@hide} */
interface ITuner {
int getProgramInformation(out RadioManager.ProgramInfo[] infoOut);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.radio;
/** @hide */
parcelable RadioManager.ProgramInfo;

View File

@@ -17,12 +17,19 @@
package android.hardware.radio;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
import java.util.List;
import java.util.Arrays;
@@ -35,6 +42,7 @@ import java.util.Arrays;
*/
@SystemApi
public class RadioManager {
private static final String TAG = "RadioManager";
/** Method return status: successful operation */
public static final int STATUS_OK = 0;
@@ -1434,23 +1442,40 @@ public class RadioManager {
public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio,
RadioTuner.Callback callback, Handler handler) {
if (callback == null) {
return null;
throw new IllegalArgumentException("callback must not be empty");
}
RadioModule module = new RadioModule(moduleId, config, withAudio, callback, handler);
if (module != null) {
if (!module.initCheck()) {
module = null;
if (mService != null) {
ITuner tuner;
try {
tuner = mService.openTuner();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return new TunerAdapter(tuner);
}
RadioModule module = new RadioModule(moduleId, config, withAudio, callback, handler);
if (!module.initCheck()) {
Log.e(TAG, "Failed to open tuner");
module = null;
}
return (RadioTuner)module;
}
private final Context mContext;
@NonNull private final Context mContext;
// TODO(b/36863239): NonNull when transitioned from native service
@Nullable private final IRadioService mService;
/**
* @hide
*/
public RadioManager(Context context) {
public RadioManager(@NonNull Context context) throws ServiceNotFoundException {
mContext = context;
boolean isServiceJava = SystemProperties.getBoolean("config.enable_java_radio", false);
mService = isServiceJava ? IRadioService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE)) : null;
}
}

View File

@@ -0,0 +1,143 @@
/**
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.radio;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Implements the RadioTuner interface by forwarding calls to radio service.
*/
class TunerAdapter extends RadioTuner {
private static final String TAG = "radio.TunerAdapter";
@NonNull private final ITuner mTuner;
TunerAdapter(ITuner tuner) {
if (tuner == null) {
throw new NullPointerException();
}
mTuner = tuner;
}
@Override
public void close() {
// TODO(b/36863239): forward to mTuner
Log.w(TAG, "Close call not implemented");
}
@Override
public int setConfiguration(RadioManager.BandConfig config) {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public int getConfiguration(RadioManager.BandConfig[] config) {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public int setMute(boolean mute) {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public boolean getMute() {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public int step(int direction, boolean skipSubChannel) {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public int scan(int direction, boolean skipSubChannel) {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public int tune(int channel, int subChannel) {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public int cancel() {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public int getProgramInformation(RadioManager.ProgramInfo[] info) {
if (info == null || info.length != 1) {
throw new IllegalArgumentException("The argument must be an array of length 1");
}
try {
return mTuner.getProgramInformation(info);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
public boolean startBackgroundScan() {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public @NonNull List<RadioManager.ProgramInfo> getProgramList(@Nullable String filter) {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public boolean isAnalogForced() {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public void setAnalogForced(boolean isForced) {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public boolean isAntennaConnected() {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
@Override
public boolean hasControl() {
// TODO(b/36863239): forward to mTuner
throw new RuntimeException("Not implemented");
}
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.radio;
import android.content.Context;
import android.hardware.radio.IRadioService;
import android.hardware.radio.ITuner;
import android.hardware.radio.RadioManager;
import android.util.Slog;
import com.android.server.SystemService;
public class RadioService extends SystemService {
// TODO(b/36863239): rename to RadioService when native service goes away
private static final String TAG = "RadioServiceJava";
public RadioService(Context context) {
super(context);
}
@Override
public void onStart() {
publishBinderService(Context.RADIO_SERVICE, new RadioServiceImpl());
Slog.v(TAG, "RadioService started");
}
private static class RadioServiceImpl extends IRadioService.Stub {
@Override
public ITuner openTuner() {
Slog.d(TAG, "openTuner()");
return new TunerImpl();
}
}
private static class TunerImpl extends ITuner.Stub {
@Override
public int getProgramInformation(RadioManager.ProgramInfo[] infoOut) {
Slog.d(TAG, "getProgramInformation()");
return RadioManager.STATUS_INVALID_OPERATION;
}
}
}

View File

@@ -96,6 +96,7 @@ import com.android.server.pm.UserManagerService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
import com.android.server.radio.RadioService;
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.retaildemo.RetailDemoModeService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
@@ -711,6 +712,8 @@ public final class SystemServer {
boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
// TODO(b/36863239): Remove when transitioned from native service.
boolean enableRadioService = SystemProperties.getBoolean("config.enable_java_radio", false);
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -1211,6 +1214,13 @@ public final class SystemServer {
mSystemServiceManager.startService(AudioService.Lifecycle.class);
traceEnd();
if (enableRadioService &&
mPackageManager.hasSystemFeature(PackageManager.FEATURE_RADIO)) {
traceBeginAndSlog("StartRadioService");
mSystemServiceManager.startService(RadioService.class);
traceEnd();
}
if (!disableNonCoreServices) {
traceBeginAndSlog("StartDockObserver");
mSystemServiceManager.startService(DockObserver.class);

33
tests/radio/Android.mk Normal file
View File

@@ -0,0 +1,33 @@
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := RadioTests
LOCAL_MODULE_TAGS := tests
# TODO(b/13282254): uncomment when b/13282254 is fixed
# LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_DEX_PREOPT := false
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.hardware.radio.tests">
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.hardware.radio.tests"
android:label="Tests for broadcast radio API" >
</instrumentation>
</manifest>

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.radio.tests;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
/**
* A test for broadcast radio API.
*/
@RunWith(AndroidJUnit4.class)
public class RadioTest {
public final Context mContext = InstrumentationRegistry.getContext();
private RadioManager mRadioManager;
private RadioTuner mRadioTuner;
private final List<RadioManager.ModuleProperties> mModules = new ArrayList<>();
@Before
public void setup() {
// check if radio is supported and skip the test if it's not
PackageManager packageManager = mContext.getPackageManager();
boolean isRadioSupported = packageManager.hasSystemFeature(PackageManager.FEATURE_RADIO);
assumeTrue(isRadioSupported);
mRadioManager = (RadioManager)mContext.getSystemService(Context.RADIO_SERVICE);
assertNotNull(mRadioManager);
int status = mRadioManager.listModules(mModules);
assertEquals(RadioManager.STATUS_OK, status);
assertFalse(mModules.isEmpty());
}
@After
public void tearDown() {
mRadioManager = null;
mModules.clear();
if (mRadioTuner != null) {
mRadioTuner.close();
mRadioTuner = null;
}
}
private void openTuner(RadioTuner.Callback callback) {
assertNull(mRadioTuner);
// find FM band and build its config
RadioManager.ModuleProperties module = mModules.get(0);
RadioManager.FmBandDescriptor fmBandDescriptor = null;
for (RadioManager.BandDescriptor band : module.getBands()) {
if (band.getType() == RadioManager.BAND_FM) {
fmBandDescriptor = (RadioManager.FmBandDescriptor)band;
break;
}
}
assertNotNull(fmBandDescriptor);
RadioManager.BandConfig fmBandConfig =
new RadioManager.FmBandConfig.Builder(fmBandDescriptor).build();
mRadioTuner = mRadioManager.openTuner(module.getId(), fmBandConfig, true, callback, null);
assertNotNull(mRadioTuner);
}
@Test
public void testOpenTuner() {
openTuner(new RadioTuner.Callback() {});
}
}