1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.hal; 18 19 import android.car.hardware.radio.CarRadioEvent; 20 import android.car.hardware.radio.CarRadioPreset; 21 import android.os.ServiceSpecificException; 22 import android.util.Log; 23 import android.hardware.radio.RadioManager; 24 25 import com.android.car.CarLog; 26 import com.android.car.vehiclenetwork.VehicleNetworkConsts; 27 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType; 28 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig; 29 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue; 30 import com.android.car.vehiclenetwork.VehiclePropValueUtil; 31 32 import java.util.LinkedList; 33 import java.util.List; 34 import java.io.PrintWriter; 35 36 /** 37 * This class exposes the Radio related features in the HAL layer. 38 * 39 * The current set of features support radio presets. The rest of the radio functionality is already 40 * covered under RadioManager API. 41 */ 42 public class RadioHalService extends HalServiceBase { 43 public static boolean DBG = true; 44 public static String TAG = CarLog.TAG_HAL + ".RadioHalService"; 45 46 private int mPresetCount = 0; 47 private VehicleHal mHal; 48 private RadioListener mListener; 49 50 public interface RadioListener { onEvent(CarRadioEvent event)51 public void onEvent(CarRadioEvent event); 52 } 53 RadioHalService(VehicleHal hal)54 public RadioHalService(VehicleHal hal) { 55 mHal = hal; 56 } 57 58 @Override init()59 public synchronized void init() { 60 } 61 62 @Override release()63 public synchronized void release() { 64 mListener = null; 65 } 66 67 @Override takeSupportedProperties( List<VehiclePropConfig> allProperties)68 public synchronized List<VehiclePropConfig> takeSupportedProperties( 69 List<VehiclePropConfig> allProperties) { 70 List<VehiclePropConfig> supported = new LinkedList<VehiclePropConfig>(); 71 for (VehiclePropConfig p : allProperties) { 72 if (handleRadioProperty(p)) { 73 supported.add(p); 74 } 75 } 76 return supported; 77 } 78 79 @Override handleHalEvents(List<VehiclePropValue> values)80 public void handleHalEvents(List<VehiclePropValue> values) { 81 if (DBG) { 82 Log.d(TAG, "handleHalEvents"); 83 } 84 RadioHalService.RadioListener radioListener = null; 85 synchronized (this) { 86 radioListener = mListener; 87 } 88 89 if (radioListener == null) { 90 Log.e(TAG, "radio listener is null, ignoring event: " + values); 91 return; 92 } 93 94 for (VehiclePropValue v : values) { 95 CarRadioEvent radioEvent = createCarRadioEvent(v); 96 if (radioEvent != null) { 97 if (DBG) { 98 Log.d(TAG, "Sending event to listener: " + radioEvent); 99 } 100 radioListener.onEvent(radioEvent); 101 } else { 102 Log.w(TAG, "Value conversion failed: " + v); 103 } 104 } 105 } 106 107 @Override dump(PrintWriter writer)108 public void dump(PrintWriter writer) { 109 writer.println("*RadioHal*"); 110 writer.println("**Supported properties**"); 111 writer.println(VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET); 112 if (mListener != null) { 113 writer.println("Hal service registered."); 114 } 115 } 116 registerListener(RadioListener listener)117 public synchronized void registerListener(RadioListener listener) { 118 if (DBG) { 119 Log.d(TAG, "registerListener"); 120 } 121 mListener = listener; 122 123 // Subscribe to all radio properties. 124 mHal.subscribeProperty(this, VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET, 0); 125 } 126 unregisterListener()127 public synchronized void unregisterListener() { 128 if (DBG) { 129 Log.d(TAG, "unregisterListener"); 130 } 131 mListener = null; 132 133 // Unsubscribe from all propreties. 134 mHal.unsubscribeProperty(this, VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET); 135 } 136 getPresetCount()137 public synchronized int getPresetCount() { 138 Log.d(TAG, "get preset count: " + mPresetCount); 139 return mPresetCount; 140 } 141 getRadioPreset(int presetNumber)142 public CarRadioPreset getRadioPreset(int presetNumber) { 143 // Check if the preset number is out of range. We should return NULL if that is the case. 144 if (DBG) { 145 Log.d(TAG, "getRadioPreset called with preset number " + presetNumber); 146 } 147 if (!isValidPresetNumber(presetNumber)) { 148 throw new IllegalArgumentException("Preset number not valid: " + presetNumber); 149 } 150 151 int[] presetArray = {presetNumber, 0, 0, 0}; 152 VehiclePropValue presetNumberValue = 153 VehiclePropValueUtil.createIntVectorValue( 154 VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET, presetArray, 0); 155 156 VehiclePropValue presetConfig; 157 try { 158 presetConfig = mHal.getVehicleNetwork().getProperty(presetNumberValue); 159 } catch (ServiceSpecificException e) { 160 Log.e(TAG, "property VEHICLE_PROPERTY_RADIO_PRESET not ready"); 161 return null; 162 } 163 // Sanity check the output from HAL. 164 if (presetConfig.getInt32ValuesCount() != 4) { 165 Log.e(TAG, "Return value does not have 4 elements: " + 166 presetConfig.getInt32ValuesList()); 167 throw new IllegalStateException( 168 "Invalid preset returned from service: " + presetConfig.getInt32ValuesList()); 169 } 170 171 int retPresetNumber = presetConfig.getInt32Values(0); 172 int retBand = presetConfig.getInt32Values(1); 173 int retChannel = presetConfig.getInt32Values(2); 174 int retSubChannel = presetConfig.getInt32Values(3); 175 if (retPresetNumber != presetNumber) { 176 Log.e(TAG, "Preset number is not the same: " + presetNumber + " vs " + retPresetNumber); 177 return null; 178 } 179 if (!isValidBand(retBand)) return null; 180 181 // Return the actual config. 182 CarRadioPreset retConfig = 183 new CarRadioPreset(retPresetNumber, retBand, retChannel, retSubChannel); 184 if (DBG) { 185 Log.d(TAG, "Preset obtained: " + retConfig); 186 } 187 return retConfig; 188 } 189 setRadioPreset(CarRadioPreset preset)190 public boolean setRadioPreset(CarRadioPreset preset) { 191 if (DBG) { 192 Log.d(TAG, "setRadioPreset with config " + preset); 193 } 194 195 if (!isValidPresetNumber(preset.getPresetNumber()) || 196 !isValidBand(preset.getBand())) { 197 return false; 198 } 199 200 VehiclePropValue setPresetValue = 201 VehiclePropValueUtil.createBuilder( 202 VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET, 203 VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4, 0) 204 .addInt32Values(preset.getPresetNumber()) 205 .addInt32Values(preset.getBand()) 206 .addInt32Values(preset.getChannel()) 207 .addInt32Values(preset.getSubChannel()) 208 .build(); 209 mHal.getVehicleNetwork().setProperty(setPresetValue); 210 return true; 211 } 212 isValidPresetNumber(int presetNumber)213 private boolean isValidPresetNumber(int presetNumber) { 214 // Check for preset number. 215 if (presetNumber < VehicleNetworkConsts.VehicleRadioConsts.VEHICLE_RADIO_PRESET_MIN_VALUE 216 || presetNumber > mPresetCount) { 217 Log.e(TAG, "Preset number not in range (1, " + mPresetCount + ") - " + presetNumber); 218 return false; 219 } 220 return true; 221 } 222 isValidBand(int band)223 private boolean isValidBand(int band) { 224 // Check for band info. 225 if (band != RadioManager.BAND_AM && 226 band != RadioManager.BAND_FM && 227 band != RadioManager.BAND_FM_HD && 228 band != RadioManager.BAND_AM_HD) { 229 Log.e(TAG, "Preset band is not valid: " + band); 230 return false; 231 } 232 return true; 233 } 234 handleRadioProperty(VehiclePropConfig property)235 private boolean handleRadioProperty(VehiclePropConfig property) { 236 switch (property.getProp()) { 237 case VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET: 238 // Extract the count of presets. 239 mPresetCount = property.getConfigArray(0); 240 Log.d(TAG, "Read presets count: " + mPresetCount); 241 return true; 242 default: 243 return false; 244 } 245 // Should never come here. 246 } 247 createCarRadioEvent(VehiclePropValue v)248 private CarRadioEvent createCarRadioEvent(VehiclePropValue v) { 249 switch (v.getProp()) { 250 case VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET: 251 if (v.getInt32ValuesCount() != 4) { 252 Log.e(TAG, "Returned a wrong array size: " + v.getInt32ValuesCount()); 253 return null; 254 } 255 256 Integer intValues[] = new Integer[4]; 257 v.getInt32ValuesList().toArray(intValues); 258 259 // Verify the correctness of the values. 260 if (!isValidPresetNumber(intValues[0]) && !isValidBand(intValues[1])) { 261 return null; 262 } 263 264 CarRadioPreset preset = 265 new CarRadioPreset(intValues[0], intValues[1], intValues[2], intValues[3]); 266 CarRadioEvent event = new CarRadioEvent(CarRadioEvent.RADIO_PRESET, preset); 267 return event; 268 default: 269 Log.e(TAG, "createCarRadioEvent: Value not supported as event: " + v); 270 return null; 271 } 272 } 273 } 274