• 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.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