blob: 97ed27ff8c29361cc2a820952ea3db8448777abb [file] [log] [blame]
Antonio Cortese4619c72017-02-02 07:53:27 -08001/*
2 * Copyright (C) 2017 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.annotation.FutureFeature;
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -070021import android.car.vms.IVmsSubscriberClient;
Antonio Cortese4619c72017-02-02 07:53:27 -080022import android.car.vms.IVmsSubscriberService;
Antonio Cortes0bd67c32017-03-13 08:02:40 -070023import android.car.vms.VmsLayer;
Antonio Cortese4619c72017-02-02 07:53:27 -080024import android.content.Context;
25import android.os.IBinder;
26import android.os.RemoteException;
27import android.util.Log;
28
29import com.android.car.hal.VmsHalService;
30import com.android.internal.annotations.GuardedBy;
31
32import java.io.PrintWriter;
33import java.util.ArrayList;
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -070034import java.util.Collections;
Antonio Cortese4619c72017-02-02 07:53:27 -080035import java.util.HashMap;
36import java.util.List;
37import java.util.Map;
asafro25a87402017-02-28 16:08:17 -080038import java.util.Set;
Antonio Cortese4619c72017-02-02 07:53:27 -080039
40/**
41 * + Receives HAL updates by implementing VmsHalService.VmsHalListener.
42 * + Offers subscriber/publisher services by implementing IVmsService.Stub.
Antonio Cortese4619c72017-02-02 07:53:27 -080043 */
44@FutureFeature
45public class VmsSubscriberService extends IVmsSubscriberService.Stub
asafro99e4f082017-02-09 13:11:42 -080046 implements CarServiceBase, VmsHalService.VmsHalSubscriberListener {
Antonio Cortese4619c72017-02-02 07:53:27 -080047 private static final boolean DBG = true;
48 private static final String PERMISSION = Car.PERMISSION_VMS_SUBSCRIBER;
49 private static final String TAG = "VmsSubscriberService";
50
51 private final Context mContext;
52 private final VmsHalService mHal;
asafro25a87402017-02-28 16:08:17 -080053
54 @GuardedBy("mSubscriberServiceLock")
Antonio Cortese4619c72017-02-02 07:53:27 -080055 private final VmsListenerManager mMessageReceivedManager = new VmsListenerManager();
asafro25a87402017-02-28 16:08:17 -080056 private final Object mSubscriberServiceLock = new Object();
Antonio Cortese4619c72017-02-02 07:53:27 -080057
58 /**
59 * Keeps track of listeners of this service.
60 */
61 class VmsListenerManager {
62 /**
63 * Allows to modify mListenerMap and mListenerDeathRecipientMap as a single unit.
64 */
asafro25a87402017-02-28 16:08:17 -080065 private final Object mListenerManagerLock = new Object();
66 @GuardedBy("mListenerManagerLock")
Antonio Cortese4619c72017-02-02 07:53:27 -080067 private final Map<IBinder, ListenerDeathRecipient> mListenerDeathRecipientMap =
68 new HashMap<>();
asafro25a87402017-02-28 16:08:17 -080069 @GuardedBy("mListenerManagerLock")
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -070070 private final Map<IBinder, IVmsSubscriberClient> mListenerMap = new HashMap<>();
Antonio Cortese4619c72017-02-02 07:53:27 -080071
72 class ListenerDeathRecipient implements IBinder.DeathRecipient {
73 private IBinder mListenerBinder;
74
75 ListenerDeathRecipient(IBinder listenerBinder) {
76 mListenerBinder = listenerBinder;
77 }
78
79 /**
80 * Listener died. Remove it from this service.
81 */
82 @Override
83 public void binderDied() {
84 if (DBG) {
85 Log.d(TAG, "binderDied " + mListenerBinder);
86 }
asafro25a87402017-02-28 16:08:17 -080087
88 // Get the Listener from the Binder
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -070089 IVmsSubscriberClient listener = mListenerMap.get(mListenerBinder);
asafro25a87402017-02-28 16:08:17 -080090
91 // Remove the listener subscriptions.
92 if (listener != null) {
93 Log.d(TAG, "Removing subscriptions for dead listener: " + listener);
94 mHal.removeDeadListener(listener);
95 } else {
96 Log.d(TAG, "Handling dead binder with no matching listener");
97
98 }
99
100 // Remove binder
Antonio Cortese4619c72017-02-02 07:53:27 -0800101 VmsListenerManager.this.removeListener(mListenerBinder);
102 }
103
104 void release() {
105 mListenerBinder.unlinkToDeath(this, 0);
106 }
107 }
108
109 public void release() {
110 for (ListenerDeathRecipient recipient : mListenerDeathRecipientMap.values()) {
111 recipient.release();
112 }
113 mListenerDeathRecipientMap.clear();
114 mListenerMap.clear();
115 }
116
117 /**
118 * Adds the listener and a death recipient associated to it.
119 *
120 * @param listener to be added.
121 * @throws IllegalArgumentException if the listener is null.
122 * @throws IllegalStateException if it was not possible to link a death recipient to the
123 * listener.
124 */
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700125 public void add(IVmsSubscriberClient listener) {
Antonio Cortese4619c72017-02-02 07:53:27 -0800126 ICarImpl.assertPermission(mContext, PERMISSION);
127 if (listener == null) {
128 Log.e(TAG, "register: listener is null.");
129 throw new IllegalArgumentException("listener cannot be null.");
130 }
131 if (DBG) {
132 Log.d(TAG, "register: " + listener);
133 }
134 IBinder listenerBinder = listener.asBinder();
asafro25a87402017-02-28 16:08:17 -0800135 synchronized (mListenerManagerLock) {
Antonio Cortese4619c72017-02-02 07:53:27 -0800136 if (mListenerMap.containsKey(listenerBinder)) {
137 // Already registered, nothing to do.
138 return;
139 }
140 ListenerDeathRecipient deathRecipient = new ListenerDeathRecipient(listenerBinder);
141 try {
142 listenerBinder.linkToDeath(deathRecipient, 0);
143 } catch (RemoteException e) {
144 Log.e(TAG, "Failed to link death for recipient. ", e);
145 throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
146 }
147 mListenerDeathRecipientMap.put(listenerBinder, deathRecipient);
148 mListenerMap.put(listenerBinder, listener);
149 }
150 }
151
152 /**
153 * Removes the listener and associated death recipient.
154 *
155 * @param listener to be removed.
156 * @throws IllegalArgumentException if listener is null.
157 */
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700158 public void remove(IVmsSubscriberClient listener) {
Antonio Cortese4619c72017-02-02 07:53:27 -0800159 if (DBG) {
160 Log.d(TAG, "unregisterListener");
161 }
162 ICarImpl.assertPermission(mContext, PERMISSION);
163 if (listener == null) {
164 Log.e(TAG, "unregister: listener is null.");
165 throw new IllegalArgumentException("Listener is null");
166 }
167 IBinder listenerBinder = listener.asBinder();
168 removeListener(listenerBinder);
169 }
170
171 // Removes the listenerBinder from the current state.
172 // The function assumes that binder will exist both in listeners and death recipients list.
173 private void removeListener(IBinder listenerBinder) {
asafro25a87402017-02-28 16:08:17 -0800174 synchronized (mListenerManagerLock) {
Antonio Cortese4619c72017-02-02 07:53:27 -0800175 boolean found = mListenerMap.remove(listenerBinder) != null;
176 if (found) {
177 mListenerDeathRecipientMap.get(listenerBinder).release();
178 mListenerDeathRecipientMap.remove(listenerBinder);
179 } else {
180 Log.e(TAG, "removeListener: listener was not previously registered.");
181 }
182 }
183 }
184
185 /**
186 * Returns list of listeners currently registered.
187 *
188 * @return list of listeners.
189 */
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700190 public List<IVmsSubscriberClient> getListeners() {
asafro25a87402017-02-28 16:08:17 -0800191 synchronized (mListenerManagerLock) {
Antonio Cortese4619c72017-02-02 07:53:27 -0800192 return new ArrayList<>(mListenerMap.values());
193 }
194 }
195 }
196
197 public VmsSubscriberService(Context context, VmsHalService hal) {
198 mContext = context;
199 mHal = hal;
200 }
201
202 // Implements CarServiceBase interface.
203 @Override
204 public void init() {
asafro99e4f082017-02-09 13:11:42 -0800205 mHal.addSubscriberListener(this);
Antonio Cortese4619c72017-02-02 07:53:27 -0800206 }
207
208 @Override
209 public void release() {
210 mMessageReceivedManager.release();
asafro99e4f082017-02-09 13:11:42 -0800211 mHal.removeSubscriberListener(this);
Antonio Cortese4619c72017-02-02 07:53:27 -0800212 }
213
214 @Override
215 public void dump(PrintWriter writer) {
216 }
217
218 // Implements IVmsService interface.
219 @Override
Antonio Cortes01a60392017-03-22 13:00:02 -0700220 public void addVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) {
asafro25a87402017-02-28 16:08:17 -0800221 synchronized (mSubscriberServiceLock) {
222 // Add the listener so it can subscribe.
223 mMessageReceivedManager.add(listener);
224
225 // Add the subscription for the layer.
asafro25a87402017-02-28 16:08:17 -0800226 mHal.addSubscription(listener, layer);
227 }
Antonio Cortese4619c72017-02-02 07:53:27 -0800228 }
229
230 @Override
Antonio Cortes01a60392017-03-22 13:00:02 -0700231 public void removeVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) {
asafro25a87402017-02-28 16:08:17 -0800232 synchronized (mSubscriberServiceLock) {
233 // Remove the subscription.
asafro25a87402017-02-28 16:08:17 -0800234 mHal.removeSubscription(listener, layer);
235
236 // Remove the listener if it has no more subscriptions.
237 if (!mHal.containsListener(listener)) {
238 mMessageReceivedManager.remove(listener);
239 }
240 }
241 }
242
243 @Override
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700244 public void addVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) {
asafro25a87402017-02-28 16:08:17 -0800245 synchronized (mSubscriberServiceLock) {
246 mMessageReceivedManager.add(listener);
247 mHal.addSubscription(listener);
248 }
249 }
250
251 @Override
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700252 public void removeVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) {
asafro25a87402017-02-28 16:08:17 -0800253 synchronized (mSubscriberServiceLock) {
254 // Remove the subscription.
255 mHal.removeSubscription(listener);
256
257 // Remove the listener if it has no more subscriptions.
258 if (!mHal.containsListener(listener)) {
259 mMessageReceivedManager.remove(listener);
260 }
261 }
Antonio Cortese4619c72017-02-02 07:53:27 -0800262 }
263
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700264 @Override
265 public List<VmsLayer> getAvailableLayers() {
266 //TODO(asafro): return the list of available layers once logic is implemented.
267 return Collections.emptyList();
268 }
269
asafro99e4f082017-02-09 13:11:42 -0800270 // Implements VmsHalSubscriberListener interface
Antonio Cortese4619c72017-02-02 07:53:27 -0800271 @Override
Antonio Cortes01a60392017-03-22 13:00:02 -0700272 public void onChange(VmsLayer layer, byte[] payload) {
asafro25a87402017-02-28 16:08:17 -0800273 if(DBG) {
274 Log.d(TAG, "Publishing a message for layer: " + layer);
275 }
276
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700277 Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
asafro25a87402017-02-28 16:08:17 -0800278
279 // If there are no listeners we're done.
280 if ((listeners == null)) {
281 return;
282 }
283
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700284 for (IVmsSubscriberClient subscriber : listeners) {
Antonio Cortese4619c72017-02-02 07:53:27 -0800285 try {
Antonio Cortes01a60392017-03-22 13:00:02 -0700286 subscriber.onVmsMessageReceived(layer, payload);
Antonio Cortese4619c72017-02-02 07:53:27 -0800287 } catch (RemoteException e) {
288 // If we could not send a record, its likely the connection snapped. Let the binder
289 // death handle the situation.
290 Log.e(TAG, "onVmsMessageReceived calling failed: ", e);
291 }
292 }
asafro25a87402017-02-28 16:08:17 -0800293
Antonio Cortese4619c72017-02-02 07:53:27 -0800294 }
295}