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 17package com.android.car; 18 19import android.car.Car; 20import android.car.hardware.radio.CarRadioEvent; 21import android.car.hardware.radio.CarRadioPreset; 22import android.car.hardware.radio.ICarRadio; 23import android.car.hardware.radio.ICarRadioEventListener; 24import android.content.Context; 25import android.content.pm.PackageManager; 26import android.os.IBinder; 27import android.os.Process; 28import android.os.RemoteException; 29import android.util.Log; 30 31import com.android.car.hal.VehicleHal; 32import com.android.car.hal.RadioHalService; 33 34import java.io.PrintWriter; 35import java.util.HashMap; 36 37public 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