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