• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.Nullable;
20 import android.car.hardware.radio.CarRadioEvent;
21 import android.car.hardware.radio.CarRadioPreset;
22 import android.hardware.radio.RadioManager;
23 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
24 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
25 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
26 import android.hardware.automotive.vehicle.V2_0.VehicleRadioConstants;
27 import android.util.Log;
28 
29 import com.android.car.CarLog;
30 
31 import java.io.PrintWriter;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.LinkedList;
35 import java.util.List;
36 
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  */
43 public class RadioHalService extends HalServiceBase {
44     public static boolean DBG = false;
45     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 {
onEvent(CarRadioEvent event)52         void onEvent(CarRadioEvent event);
53     }
54 
RadioHalService(VehicleHal hal)55     public RadioHalService(VehicleHal hal) {
56         mHal = hal;
57     }
58 
59     @Override
init()60     public synchronized void init() {
61     }
62 
63     @Override
release()64     public synchronized void release() {
65         mListener = null;
66     }
67 
68     @Override
takeSupportedProperties( Collection<VehiclePropConfig> allProperties)69     public synchronized Collection<VehiclePropConfig> takeSupportedProperties(
70             Collection<VehiclePropConfig> allProperties) {
71         Collection<VehiclePropConfig> supported = new LinkedList<>();
72         for (VehiclePropConfig p : allProperties) {
73             if (handleRadioProperty(p)) {
74                 supported.add(p);
75             }
76         }
77         return supported;
78     }
79 
80     @Override
handleHalEvents(List<VehiclePropValue> values)81     public void handleHalEvents(List<VehiclePropValue> values) {
82         if (DBG) {
83             Log.d(TAG, "handleHalEvents");
84         }
85         RadioHalService.RadioListener radioListener;
86         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
dump(PrintWriter writer)109     public void dump(PrintWriter writer) {
110         writer.println("*RadioHal*");
111         writer.println("**Supported properties**");
112         writer.println(VehicleProperty.RADIO_PRESET);
113         if (mListener != null) {
114             writer.println("Hal service registered.");
115         }
116     }
117 
registerListener(RadioListener listener)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.
125         mHal.subscribeProperty(this, VehicleProperty.RADIO_PRESET);
126     }
127 
unregisterListener()128     public synchronized void unregisterListener() {
129         if (DBG) {
130             Log.d(TAG, "unregisterListener");
131         }
132         mListener = null;
133 
134         // Unsubscribe from all properties.
135         mHal.unsubscribeProperty(this, VehicleProperty.RADIO_PRESET);
136     }
137 
getPresetCount()138     public synchronized int getPresetCount() {
139         Log.d(TAG, "get preset count: " + mPresetCount);
140         return mPresetCount;
141     }
142 
143     @Nullable
getRadioPreset(int presetNumber)144     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 
153         VehiclePropValue presetNumberValue = new VehiclePropValue();
154         presetNumberValue.prop = VehicleProperty.RADIO_PRESET;
155         presetNumberValue.value.int32Values.addAll(Arrays.asList(presetNumber, 0, 0, 0));
156 
157         VehiclePropValue presetConfig;
158         try {
159             presetConfig = mHal.get(presetNumberValue);
160         } catch (PropertyTimeoutException e) {
161             Log.e(TAG, "property VehicleProperty.RADIO_PRESET not ready", e);
162             return null;
163         }
164         // Sanity check the output from HAL.
165         if (presetConfig.value.int32Values.size() != 4) {
166             Log.e(TAG, "Return value does not have 4 elements: " +
167                 Arrays.toString(presetConfig.value.int32Values.toArray()));
168             throw new IllegalStateException(
169                 "Invalid preset returned from service: "
170                         + Arrays.toString(presetConfig.value.int32Values.toArray()));
171         }
172 
173         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);
177         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 
setRadioPreset(CarRadioPreset preset)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 
202         try {
203             mHal.set(VehicleProperty.RADIO_PRESET).to(new int[] {
204                     preset.getPresetNumber(),
205                     preset.getBand(),
206                     preset.getChannel(),
207                     preset.getSubChannel()});
208         } catch (PropertyTimeoutException e) {
209             Log.e(CarLog.TAG_POWER, "cannot set to RADIO_PRESET", e);
210             return false;
211         }
212         return true;
213     }
214 
isValidPresetNumber(int presetNumber)215     private boolean isValidPresetNumber(int presetNumber) {
216         // Check for preset number.
217         if (presetNumber < VehicleRadioConstants.VEHICLE_RADIO_PRESET_MIN_VALUE
218             || presetNumber > mPresetCount) {
219             Log.e(TAG, "Preset number not in range (1, " + mPresetCount + ") - " + presetNumber);
220             return false;
221         }
222         return true;
223     }
224 
isValidBand(int band)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 
handleRadioProperty(VehiclePropConfig property)237     private boolean handleRadioProperty(VehiclePropConfig property) {
238         switch (property.prop) {
239             case VehicleProperty.RADIO_PRESET:
240                 // Extract the count of presets.
241                 mPresetCount = property.configArray.get(0);
242                 Log.d(TAG, "Read presets count: " + mPresetCount);
243                 return true;
244             default:
245                 return false;
246         }
247         // Should never come here.
248     }
249 
createCarRadioEvent(VehiclePropValue v)250     private CarRadioEvent createCarRadioEvent(VehiclePropValue v) {
251         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);
256                     return null;
257                 }
258 
259                 Integer intValues[] = new Integer[4];
260                 v.value.int32Values.toArray(intValues);
261 
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 }
277