Home | History | Annotate | Download | only in hal
      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 {
     52         void onEvent(CarRadioEvent event);
     53     }
     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
     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
     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
    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 
    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 
    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 
    138     public synchronized int getPresetCount() {
    139         Log.d(TAG, "get preset count: " + mPresetCount);
    140         return mPresetCount;
    141     }
    142 
    143     @Nullable
    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 
    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 
    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 
    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) {
    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 
    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