blob: 68423d0495a7a3ca7455dcfc8ed8771b9f318cb6 [file] [log] [blame]
Sanket Agarwal3cf096a2015-10-13 14:46:31 -07001/*
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
17package com.android.car.hal;
18
Keun-young Parke54ac272016-02-16 19:02:18 -080019import android.car.hardware.radio.CarRadioEvent;
20import android.car.hardware.radio.CarRadioPreset;
Keun-young Parkba485482016-03-24 13:24:31 -070021import android.os.ServiceSpecificException;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070022import android.util.Log;
23import android.hardware.radio.RadioManager;
24
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070025import com.android.car.CarLog;
26import com.android.car.vehiclenetwork.VehicleNetworkConsts;
27import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
28import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
29import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
30import com.android.car.vehiclenetwork.VehiclePropValueUtil;
31
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070032import java.util.LinkedList;
33import java.util.List;
34import 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 */
42public class RadioHalService extends HalServiceBase {
Vitalii Tomkiv1b1247b2016-09-30 11:27:19 -070043 public static boolean DBG = false;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070044 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 {
51 public void onEvent(CarRadioEvent event);
52 }
53
54 public RadioHalService(VehicleHal hal) {
55 mHal = hal;
56 }
57
58 @Override
59 public synchronized void init() {
60 }
61
62 @Override
63 public synchronized void release() {
64 mListener = null;
65 }
66
67 @Override
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
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
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
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
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
137 public synchronized int getPresetCount() {
138 Log.d(TAG, "get preset count: " + mPresetCount);
139 return mPresetCount;
140 }
141
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
Keun-young Parkba485482016-03-24 13:24:31 -0700156 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 }
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700163 // 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
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();
Keun-young Parke78bf532016-04-25 18:59:22 -0700209 try {
210 mHal.getVehicleNetwork().setProperty(setPresetValue);
211 } catch (ServiceSpecificException e) {
212 Log.e(CarLog.TAG_POWER, "cannot set to RADIO_PRESET", e);
213 return false;
214 }
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700215 return true;
216 }
217
218 private boolean isValidPresetNumber(int presetNumber) {
219 // Check for preset number.
220 if (presetNumber < VehicleNetworkConsts.VehicleRadioConsts.VEHICLE_RADIO_PRESET_MIN_VALUE
221 || presetNumber > mPresetCount) {
222 Log.e(TAG, "Preset number not in range (1, " + mPresetCount + ") - " + presetNumber);
223 return false;
224 }
225 return true;
226 }
227
228 private boolean isValidBand(int band) {
229 // Check for band info.
230 if (band != RadioManager.BAND_AM &&
231 band != RadioManager.BAND_FM &&
232 band != RadioManager.BAND_FM_HD &&
233 band != RadioManager.BAND_AM_HD) {
234 Log.e(TAG, "Preset band is not valid: " + band);
235 return false;
236 }
237 return true;
238 }
239
240 private boolean handleRadioProperty(VehiclePropConfig property) {
241 switch (property.getProp()) {
242 case VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET:
243 // Extract the count of presets.
Keun-young Park0727f952015-12-21 14:30:07 -0800244 mPresetCount = property.getConfigArray(0);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700245 Log.d(TAG, "Read presets count: " + mPresetCount);
246 return true;
247 default:
248 return false;
249 }
250 // Should never come here.
251 }
252
253 private CarRadioEvent createCarRadioEvent(VehiclePropValue v) {
254 switch (v.getProp()) {
255 case VehicleNetworkConsts.VEHICLE_PROPERTY_RADIO_PRESET:
256 if (v.getInt32ValuesCount() != 4) {
257 Log.e(TAG, "Returned a wrong array size: " + v.getInt32ValuesCount());
258 return null;
259 }
260
261 Integer intValues[] = new Integer[4];
262 v.getInt32ValuesList().toArray(intValues);
263
264 // Verify the correctness of the values.
265 if (!isValidPresetNumber(intValues[0]) && !isValidBand(intValues[1])) {
266 return null;
267 }
268
269 CarRadioPreset preset =
270 new CarRadioPreset(intValues[0], intValues[1], intValues[2], intValues[3]);
271 CarRadioEvent event = new CarRadioEvent(CarRadioEvent.RADIO_PRESET, preset);
272 return event;
273 default:
274 Log.e(TAG, "createCarRadioEvent: Value not supported as event: " + v);
275 return null;
276 }
277 }
278}