blob: 3e5e6df3b6c4ee2b799e06c3670a66f8df8652db [file] [log] [blame]
Sanket Agarwal3cf096a2015-10-13 14:46:31 -07001/*
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
Keun-young Parke54ac272016-02-16 19:02:18 -080019import 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;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070024import android.content.Context;
25import android.content.pm.PackageManager;
26import android.os.IBinder;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070027import android.os.Process;
28import android.os.RemoteException;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070029import android.util.Log;
30
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070031import com.android.car.hal.VehicleHal;
32import com.android.car.hal.RadioHalService;
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070033
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";
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070041
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
Sanket Agarwal3cf096a2015-10-13 14:46:31 -070099 public int getPresetCount() {
100 return mRadioHal.getPresetCount();
101 }
102
103 @Override
Keun-young Parke54ac272016-02-16 19:02:18 -0800104 public synchronized void registerListener(ICarRadioEventListener listener) {
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700105 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 &&
Keun-young Parke54ac272016-02-16 19:02:18 -0800205 mContext.checkCallingOrSelfPermission(Car.PERMISSION_CAR_RADIO) !=
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700206 PackageManager.PERMISSION_GRANTED) {
207 throw new SecurityException("requires system app or " +
Keun-young Parke54ac272016-02-16 19:02:18 -0800208 Car.PERMISSION_CAR_RADIO);
Sanket Agarwal3cf096a2015-10-13 14:46:31 -0700209 }
210 }
211}