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 CarRadioService(Context context, RadioHalService radioHal)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 RadioDeathRecipient(IBinder listenerBinder)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 binderDied()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 release()74 void release() { 75 mListenerBinder.unlinkToDeath(this, 0); 76 } 77 } 78 79 @Override init()80 public synchronized void init() { 81 } 82 83 @Override release()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 dump(PrintWriter writer)94 public void dump(PrintWriter writer) { 95 } 96 97 @Override getPresetCount()98 public int getPresetCount() { 99 return mRadioHal.getPresetCount(); 100 } 101 102 @Override registerListener(ICarRadioEventListener listener)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 unregisterListener(ICarRadioEventListener listener)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. unregisterListenerLocked(IBinder listenerBinder)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 getPreset(int index)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 setPreset(CarRadioPreset preset)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 onEvent(CarRadioEvent event)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 checkRadioPremissions()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