blob: 68423d0495a7a3ca7455dcfc8ed8771b9f318cb6 [file] [log] [blame]
/*
* Copyright (C) 2015 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.car.hal;
import android.car.hardware.radio.CarRadioEvent;
import android.car.hardware.radio.CarRadioPreset;
import android.os.ServiceSpecificException;
import android.util.Log;
import android.hardware.radio.RadioManager;
import com.android.car.CarLog;
import com.android.car.vehiclenetwork.VehicleNetworkConsts;
import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
import com.android.car.vehiclenetwork.VehiclePropValueUtil;
import java.util.LinkedList;
import java.util.List;
import java.io.PrintWriter;
/**
* This class exposes the Radio related features in the HAL layer.
*
* The current set of features support radio presets. The rest of the radio functionality is already
* covered under RadioManager API.
*/
public class RadioHalService extends HalServiceBase {
public static boolean DBG = false;
public static String TAG = CarLog.TAG_HAL + ".RadioHalService";
private int mPresetCount = 0;
private VehicleHal mHal;
private RadioListener mListener;
public interface RadioListener {
public void onEvent(CarRadioEvent event);
}
public RadioHalService(VehicleHal hal) {
mHal = hal;
}
@Override
public synchronized void init() {
}
@Override
public synchronized void release() {
mListener = null;
}
@Override
public synchronized List<VehiclePropConfig> takeSupportedProperties(
List<VehiclePropConfig> allProperties) {
List<VehiclePropConfig> supported = new LinkedList<VehiclePropConfig>();
for (VehiclePropConfig p : allProperties) {
if (handleRadioProperty(p)) {
supported.add(p);
}
}
return supported;
}
@Override
public void handleHalEvents(List<VehiclePropValue> values) {
if (DBG) {
Log.d(TAG, "handleHalEvents");
}
RadioHalService.RadioListener radioListener = null;
synchronized (this) {
radioListener = mListener;
}
if (radioListener == null) {
Log.e(TAG, "radio listener is null, ignoring event: " + values);
return;
}
for (VehiclePropValue v : values) {
CarRadioEvent radioEvent = createCarRadioEvent(v);
if (radioEvent != null) {
if (DBG) {
Log.d(TAG, "Sending event to listener: " + radioEvent);
}
radioListener.onEvent(radioEvent);
} else {
Log.w(TAG, "Value conversion failed: " + v);
}
}
}
@Override
public void dump(PrintWriter writer) {
writer.println("*RadioHal*");
writer.println("**Supported properties**");
writer.println(VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET);
if (mListener != null) {
writer.println("Hal service registered.");
}
}
public synchronized void registerListener(RadioListener listener) {
if (DBG) {
Log.d(TAG, "registerListener");
}
mListener = listener;
// Subscribe to all radio properties.
mHal.subscribeProperty(this, VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET, 0);
}
public synchronized void unregisterListener() {
if (DBG) {
Log.d(TAG, "unregisterListener");
}
mListener = null;
// Unsubscribe from all propreties.
mHal.unsubscribeProperty(this, VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET);
}
public synchronized int getPresetCount() {
Log.d(TAG, "get preset count: " + mPresetCount);
return mPresetCount;
}
public CarRadioPreset getRadioPreset(int presetNumber) {
// Check if the preset number is out of range. We should return NULL if that is the case.
if (DBG) {
Log.d(TAG, "getRadioPreset called with preset number " + presetNumber);
}
if (!isValidPresetNumber(presetNumber)) {
throw new IllegalArgumentException("Preset number not valid: " + presetNumber);
}
int[] presetArray = {presetNumber, 0, 0, 0};
VehiclePropValue presetNumberValue =
VehiclePropValueUtil.createIntVectorValue(
VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET, presetArray, 0);
VehiclePropValue presetConfig;
try {
presetConfig = mHal.getVehicleNetwork().getProperty(presetNumberValue);
} catch (ServiceSpecificException e) {
Log.e(TAG, "property VEHICLE_PROPERTY_RADIO_PRESET not ready");
return null;
}
// Sanity check the output from HAL.
if (presetConfig.getInt32ValuesCount() != 4) {
Log.e(TAG, "Return value does not have 4 elements: " +
presetConfig.getInt32ValuesList());
throw new IllegalStateException(
"Invalid preset returned from service: " + presetConfig.getInt32ValuesList());
}
int retPresetNumber = presetConfig.getInt32Values(0);
int retBand = presetConfig.getInt32Values(1);
int retChannel = presetConfig.getInt32Values(2);
int retSubChannel = presetConfig.getInt32Values(3);
if (retPresetNumber != presetNumber) {
Log.e(TAG, "Preset number is not the same: " + presetNumber + " vs " + retPresetNumber);
return null;
}
if (!isValidBand(retBand)) return null;
// Return the actual config.
CarRadioPreset retConfig =
new CarRadioPreset(retPresetNumber, retBand, retChannel, retSubChannel);
if (DBG) {
Log.d(TAG, "Preset obtained: " + retConfig);
}
return retConfig;
}
public boolean setRadioPreset(CarRadioPreset preset) {
if (DBG) {
Log.d(TAG, "setRadioPreset with config " + preset);
}
if (!isValidPresetNumber(preset.getPresetNumber()) ||
!isValidBand(preset.getBand())) {
return false;
}
VehiclePropValue setPresetValue =
VehiclePropValueUtil.createBuilder(
VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET,
VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4, 0)
.addInt32Values(preset.getPresetNumber())
.addInt32Values(preset.getBand())
.addInt32Values(preset.getChannel())
.addInt32Values(preset.getSubChannel())
.build();
try {
mHal.getVehicleNetwork().setProperty(setPresetValue);
} catch (ServiceSpecificException e) {
Log.e(CarLog.TAG_POWER, "cannot set to RADIO_PRESET", e);
return false;
}
return true;
}
private boolean isValidPresetNumber(int presetNumber) {
// Check for preset number.
if (presetNumber < VehicleNetworkConsts.VehicleRadioConsts.VEHICLE_RADIO_PRESET_MIN_VALUE
|| presetNumber > mPresetCount) {
Log.e(TAG, "Preset number not in range (1, " + mPresetCount + ") - " + presetNumber);
return false;
}
return true;
}
private boolean isValidBand(int band) {
// Check for band info.
if (band != RadioManager.BAND_AM &&
band != RadioManager.BAND_FM &&
band != RadioManager.BAND_FM_HD &&
band != RadioManager.BAND_AM_HD) {
Log.e(TAG, "Preset band is not valid: " + band);
return false;
}
return true;
}
private boolean handleRadioProperty(VehiclePropConfig property) {
switch (property.getProp()) {
case VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET:
// Extract the count of presets.
mPresetCount = property.getConfigArray(0);
Log.d(TAG, "Read presets count: " + mPresetCount);
return true;
default:
return false;
}
// Should never come here.
}
private CarRadioEvent createCarRadioEvent(VehiclePropValue v) {
switch (v.getProp()) {
case VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET:
if (v.getInt32ValuesCount() != 4) {
Log.e(TAG, "Returned a wrong array size: " + v.getInt32ValuesCount());
return null;
}
Integer intValues[] = new Integer[4];
v.getInt32ValuesList().toArray(intValues);
// Verify the correctness of the values.
if (!isValidPresetNumber(intValues[0]) && !isValidBand(intValues[1])) {
return null;
}
CarRadioPreset preset =
new CarRadioPreset(intValues[0], intValues[1], intValues[2], intValues[3]);
CarRadioEvent event = new CarRadioEvent(CarRadioEvent.RADIO_PRESET, preset);
return event;
default:
Log.e(TAG, "createCarRadioEvent: Value not supported as event: " + v);
return null;
}
}
}