blob: 8cd8687b27832fdb8c1f95b9f8562ef1590c877a [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
Pavel Maltsev0d07c762016-11-03 16:40:15 -070019import android.annotation.Nullable;
Keun-young Parke54ac272016-02-16 19:02:18 -080020import android.car.hardware.radio.CarRadioEvent;
21import android.car.hardware.radio.CarRadioPreset;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070022import android.hardware.radio.RadioManager;
Pavel Maltsev0d07c762016-11-03 16:40:15 -070023import android.hardware.vehicle.V2_0.VehiclePropConfig;
24import android.hardware.vehicle.V2_0.VehiclePropValue;
25import android.hardware.vehicle.V2_0.VehicleProperty;
26import android.hardware.vehicle.V2_0.VehicleRadioConstants;
27import android.util.Log;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070028
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070029import com.android.car.CarLog;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070030
Pavel Maltsev0d07c762016-11-03 16:40:15 -070031import java.io.PrintWriter;
32import java.util.Arrays;
33import java.util.Collection;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070034import java.util.LinkedList;
35import java.util.List;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070036
37/**
38 * This class exposes the Radio related features in the HAL layer.
39 *
40 * The current set of features support radio presets. The rest of the radio functionality is already
41 * covered under RadioManager API.
42 */
43public class RadioHalService extends HalServiceBase {
Vitalii Tomkiv1b1247b2016-09-30 11:27:19 -070044 public static boolean DBG = false;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070045 public static String TAG = CarLog.TAG_HAL + ".RadioHalService";
46
47 private int mPresetCount = 0;
48 private VehicleHal mHal;
49 private RadioListener mListener;
50
51 public interface RadioListener {
Pavel Maltsev0d07c762016-11-03 16:40:15 -070052 void onEvent(CarRadioEvent event);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070053 }
54
55 public RadioHalService(VehicleHal hal) {
56 mHal = hal;
57 }
58
59 @Override
60 public synchronized void init() {
61 }
62
63 @Override
64 public synchronized void release() {
65 mListener = null;
66 }
67
68 @Override
Pavel Maltsev0d07c762016-11-03 16:40:15 -070069 public synchronized Collection<VehiclePropConfig> takeSupportedProperties(
70 Collection<VehiclePropConfig> allProperties) {
71 Collection<VehiclePropConfig> supported = new LinkedList<>();
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070072 for (VehiclePropConfig p : allProperties) {
73 if (handleRadioProperty(p)) {
74 supported.add(p);
75 }
76 }
77 return supported;
78 }
79
80 @Override
81 public void handleHalEvents(List<VehiclePropValue> values) {
82 if (DBG) {
83 Log.d(TAG, "handleHalEvents");
84 }
Pavel Maltsev0d07c762016-11-03 16:40:15 -070085 RadioHalService.RadioListener radioListener;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070086 synchronized (this) {
87 radioListener = mListener;
88 }
89
90 if (radioListener == null) {
91 Log.e(TAG, "radio listener is null, ignoring event: " + values);
92 return;
93 }
94
95 for (VehiclePropValue v : values) {
96 CarRadioEvent radioEvent = createCarRadioEvent(v);
97 if (radioEvent != null) {
98 if (DBG) {
99 Log.d(TAG, "Sending event to listener: " + radioEvent);
100 }
101 radioListener.onEvent(radioEvent);
102 } else {
103 Log.w(TAG, "Value conversion failed: " + v);
104 }
105 }
106 }
107
108 @Override
109 public void dump(PrintWriter writer) {
110 writer.println("*RadioHal*");
111 writer.println("**Supported properties**");
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700112 writer.println(VehicleProperty.RADIO_PRESET);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700113 if (mListener != null) {
114 writer.println("Hal service registered.");
115 }
116 }
117
118 public synchronized void registerListener(RadioListener listener) {
119 if (DBG) {
120 Log.d(TAG, "registerListener");
121 }
122 mListener = listener;
123
124 // Subscribe to all radio properties.
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700125 mHal.subscribeProperty(this, VehicleProperty.RADIO_PRESET, 0);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700126 }
127
128 public synchronized void unregisterListener() {
129 if (DBG) {
130 Log.d(TAG, "unregisterListener");
131 }
132 mListener = null;
133
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700134 // Unsubscribe from all properties.
135 mHal.unsubscribeProperty(this, VehicleProperty.RADIO_PRESET);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700136 }
137
138 public synchronized int getPresetCount() {
139 Log.d(TAG, "get preset count: " + mPresetCount);
140 return mPresetCount;
141 }
142
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700143 @Nullable
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700144 public CarRadioPreset getRadioPreset(int presetNumber) {
145 // Check if the preset number is out of range. We should return NULL if that is the case.
146 if (DBG) {
147 Log.d(TAG, "getRadioPreset called with preset number " + presetNumber);
148 }
149 if (!isValidPresetNumber(presetNumber)) {
150 throw new IllegalArgumentException("Preset number not valid: " + presetNumber);
151 }
152
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700153 VehiclePropValue presetNumberValue = new VehiclePropValue();
154 presetNumberValue.prop = VehicleProperty.RADIO_PRESET;
155 presetNumberValue.value.int32Values.addAll(Arrays.asList(presetNumber, 0, 0, 0));
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700156
Keun-young Parkba485482016-03-24 13:24:31 -0700157 VehiclePropValue presetConfig;
158 try {
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700159 presetConfig = mHal.get(presetNumberValue);
160 } catch (PropertyTimeoutException e) {
161 Log.e(TAG, "property VehicleProperty.RADIO_PRESET not ready", e);
Keun-young Parkba485482016-03-24 13:24:31 -0700162 return null;
163 }
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700164 // Sanity check the output from HAL.
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700165 if (presetConfig.value.int32Values.size() != 4) {
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700166 Log.e(TAG, "Return value does not have 4 elements: " +
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700167 Arrays.toString(presetConfig.value.int32Values.toArray()));
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700168 throw new IllegalStateException(
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700169 "Invalid preset returned from service: "
170 + Arrays.toString(presetConfig.value.int32Values.toArray()));
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700171 }
172
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700173 int retPresetNumber = presetConfig.value.int32Values.get(0);
174 int retBand = presetConfig.value.int32Values.get(1);
175 int retChannel = presetConfig.value.int32Values.get(2);
176 int retSubChannel = presetConfig.value.int32Values.get(3);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700177 if (retPresetNumber != presetNumber) {
178 Log.e(TAG, "Preset number is not the same: " + presetNumber + " vs " + retPresetNumber);
179 return null;
180 }
181 if (!isValidBand(retBand)) return null;
182
183 // Return the actual config.
184 CarRadioPreset retConfig =
185 new CarRadioPreset(retPresetNumber, retBand, retChannel, retSubChannel);
186 if (DBG) {
187 Log.d(TAG, "Preset obtained: " + retConfig);
188 }
189 return retConfig;
190 }
191
192 public boolean setRadioPreset(CarRadioPreset preset) {
193 if (DBG) {
194 Log.d(TAG, "setRadioPreset with config " + preset);
195 }
196
197 if (!isValidPresetNumber(preset.getPresetNumber()) ||
198 !isValidBand(preset.getBand())) {
199 return false;
200 }
201
Keun-young Parke78bf532016-04-25 18:59:22 -0700202 try {
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700203 mHal.set(VehicleProperty.RADIO_PRESET).to(new int[] {
204 preset.getPresetNumber(),
205 preset.getBand(),
206 preset.getChannel(),
207 preset.getSubChannel()});
208 } catch (PropertyTimeoutException e) {
Keun-young Parke78bf532016-04-25 18:59:22 -0700209 Log.e(CarLog.TAG_POWER, "cannot set to RADIO_PRESET", e);
210 return false;
211 }
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700212 return true;
213 }
214
215 private boolean isValidPresetNumber(int presetNumber) {
216 // Check for preset number.
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700217 if (presetNumber < VehicleRadioConstants.VEHICLE_RADIO_PRESET_MIN_VALUE
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700218 || presetNumber > mPresetCount) {
219 Log.e(TAG, "Preset number not in range (1, " + mPresetCount + ") - " + presetNumber);
220 return false;
221 }
222 return true;
223 }
224
225 private boolean isValidBand(int band) {
226 // Check for band info.
227 if (band != RadioManager.BAND_AM &&
228 band != RadioManager.BAND_FM &&
229 band != RadioManager.BAND_FM_HD &&
230 band != RadioManager.BAND_AM_HD) {
231 Log.e(TAG, "Preset band is not valid: " + band);
232 return false;
233 }
234 return true;
235 }
236
237 private boolean handleRadioProperty(VehiclePropConfig property) {
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700238 switch (property.prop) {
239 case VehicleProperty.RADIO_PRESET:
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700240 // Extract the count of presets.
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700241 mPresetCount = property.configArray.get(0);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700242 Log.d(TAG, "Read presets count: " + mPresetCount);
243 return true;
244 default:
245 return false;
246 }
247 // Should never come here.
248 }
249
250 private CarRadioEvent createCarRadioEvent(VehiclePropValue v) {
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700251 switch (v.prop) {
252 case VehicleProperty.RADIO_PRESET:
253 int vecSize = v.value.int32Values.size();
254 if (vecSize != 4) {
255 Log.e(TAG, "Returned a wrong array size: " + vecSize);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700256 return null;
257 }
258
259 Integer intValues[] = new Integer[4];
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700260 v.value.int32Values.toArray(intValues);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700261
262 // Verify the correctness of the values.
263 if (!isValidPresetNumber(intValues[0]) && !isValidBand(intValues[1])) {
264 return null;
265 }
266
267 CarRadioPreset preset =
268 new CarRadioPreset(intValues[0], intValues[1], intValues[2], intValues[3]);
269 CarRadioEvent event = new CarRadioEvent(CarRadioEvent.RADIO_PRESET, preset);
270 return event;
271 default:
272 Log.e(TAG, "createCarRadioEvent: Value not supported as event: " + v);
273 return null;
274 }
275 }
276}