Home | History | Annotate | Download | only in car
      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;
     18 
     19 import android.car.Car;
     20 import android.car.hardware.radio.CarRadioEvent;
     21 import android.car.hardware.radio.CarRadioPreset;
     22 import android.car.hardware.radio.ICarRadio;
     23 import android.car.hardware.radio.ICarRadioEventListener;
     24 import android.content.Context;
     25 import android.content.pm.PackageManager;
     26 import android.os.IBinder;
     27 import android.os.Process;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 
     31 import com.android.car.hal.VehicleHal;
     32 import com.android.car.hal.RadioHalService;
     33 
     34 import java.io.PrintWriter;
     35 import java.util.HashMap;
     36 
     37 public class CarRadioService extends ICarRadio.Stub
     38         implements CarServiceBase, RadioHalService.RadioListener {
     39     public static boolean DBG = true;
     40     public static String TAG = CarLog.TAG_RADIO + ".CarRadioService";
     41 
     42     private RadioHalService mRadioHal;
     43     private final HashMap<IBinder, ICarRadioEventListener> mListenersMap =
     44       new HashMap<IBinder, ICarRadioEventListener>();
     45     private final HashMap<IBinder, RadioDeathRecipient> mDeathRecipientMap =
     46         new HashMap<IBinder, RadioDeathRecipient>();
     47     private final Context mContext;
     48 
     49     public CarRadioService(Context context) {
     50         mRadioHal = VehicleHal.getInstance().getRadioHal();
     51         mContext = context;
     52     }
     53 
     54     class RadioDeathRecipient implements IBinder.DeathRecipient {
     55         private String TAG = CarRadioService.TAG + ".RadioDeathRecipient";
     56         private IBinder mListenerBinder;
     57 
     58         RadioDeathRecipient(IBinder listenerBinder) {
     59             mListenerBinder = listenerBinder;
     60         }
     61 
     62         /**
     63          * Client died. Remove the listener from HAL service and unregister if this is the last
     64          * client.
     65          */
     66         @Override
     67         public void binderDied() {
     68             if (DBG) {
     69                 Log.d(TAG, "binderDied " + mListenerBinder);
     70             }
     71             mListenerBinder.unlinkToDeath(this, 0);
     72             CarRadioService.this.unregisterListenerLocked(mListenerBinder);
     73         }
     74 
     75         void release() {
     76             mListenerBinder.unlinkToDeath(this, 0);
     77         }
     78     }
     79 
     80     @Override
     81     public synchronized void init() {
     82     }
     83 
     84     @Override
     85     public synchronized void release() {
     86         for (IBinder listenerBinder : mListenersMap.keySet()) {
     87             RadioDeathRecipient deathRecipient = mDeathRecipientMap.get(listenerBinder);
     88             deathRecipient.release();
     89         }
     90         mDeathRecipientMap.clear();
     91         mListenersMap.clear();
     92     }
     93 
     94     @Override
     95     public void dump(PrintWriter writer) {
     96     }
     97 
     98     @Override
     99     public int getPresetCount() {
    100         return mRadioHal.getPresetCount();
    101     }
    102 
    103     @Override
    104     public synchronized void registerListener(ICarRadioEventListener listener) {
    105         if (DBG) {
    106             Log.d(TAG, "registerListener");
    107         }
    108         if (listener == null) {
    109             Log.e(TAG, "registerListener: Listener is null.");
    110             throw new IllegalStateException("listener cannot be null.");
    111         }
    112 
    113         IBinder listenerBinder = listener.asBinder();
    114         if (mListenersMap.containsKey(listenerBinder)) {
    115             // Already registered, nothing to do.
    116             return;
    117         }
    118 
    119         RadioDeathRecipient deathRecipient = new RadioDeathRecipient(listenerBinder);
    120         try {
    121             listenerBinder.linkToDeath(deathRecipient, 0);
    122         } catch (RemoteException e) {
    123             Log.e(TAG, "Failed to link death for recipient. " + e);
    124             throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
    125         }
    126         mDeathRecipientMap.put(listenerBinder, deathRecipient);
    127 
    128         if (mListenersMap.isEmpty()) {
    129             mRadioHal.registerListener(this);
    130         }
    131 
    132         mListenersMap.put(listenerBinder, listener);
    133     }
    134 
    135     @Override
    136     public synchronized void unregisterListener(ICarRadioEventListener listener) {
    137         if (DBG) {
    138             Log.d(TAG, "unregisterListener");
    139         }
    140 
    141         if (listener == null) {
    142             Log.e(TAG, "unregisterListener: Listener is null.");
    143             throw new IllegalArgumentException("Listener is null");
    144         }
    145 
    146         IBinder listenerBinder = listener.asBinder();
    147         if (!mListenersMap.containsKey(listenerBinder)) {
    148             Log.e(TAG, "unregisterListener: Listener was not previously registered.");
    149         }
    150         unregisterListenerLocked(listenerBinder);
    151     }
    152 
    153     // Removes the listenerBinder from the current state.
    154     // The function assumes that the binder will exist both in listeners and death recipients list.
    155     private void unregisterListenerLocked(IBinder listenerBinder) {
    156         Object status = mListenersMap.remove(listenerBinder);
    157         if (status == null) throw new IllegalStateException(
    158             "Map must contain the event listener.");
    159 
    160         // If there is a state muck up, the release() call will throw an exception automagically.
    161         mDeathRecipientMap.get(listenerBinder).release();
    162         mDeathRecipientMap.remove(listenerBinder);
    163 
    164         if (mListenersMap.isEmpty()) {
    165             mRadioHal.unregisterListener();
    166         }
    167     }
    168 
    169     @Override
    170     public CarRadioPreset getPreset(int index) {
    171         if (DBG) {
    172             Log.d(TAG, "getPreset " + index);
    173         }
    174         return mRadioHal.getRadioPreset(index);
    175     }
    176 
    177     @Override
    178     public boolean setPreset(CarRadioPreset preset) {
    179         checkRadioPremissions();
    180         if (DBG) {
    181             Log.d(TAG, "setPreset " + preset);
    182         }
    183         boolean status = mRadioHal.setRadioPreset(preset);
    184         if (status == false) {
    185             Log.e(TAG, "setPreset failed!");
    186         }
    187         return status;
    188     }
    189 
    190     @Override
    191     public synchronized void onEvent(CarRadioEvent event) {
    192         for (ICarRadioEventListener l : mListenersMap.values()) {
    193             try {
    194                 l.onEvent(event);
    195             } catch (RemoteException ex) {
    196                 // If we could not send a record, its likely the connection snapped. Let the binder
    197                 // death handle the situation.
    198                 Log.e(TAG, "onEvent calling failed: " + ex);
    199             }
    200         }
    201     }
    202 
    203     private void checkRadioPremissions() {
    204         if (getCallingUid() != Process.SYSTEM_UID &&
    205             mContext.checkCallingOrSelfPermission(Car.PERMISSION_CAR_RADIO) !=
    206             PackageManager.PERMISSION_GRANTED) {
    207             throw new SecurityException("requires system app or " +
    208                 Car.PERMISSION_CAR_RADIO);
    209         }
    210     }
    211 }
    212