1 /*
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 
17 package com.android.car;
18 
19 import android.car.Car;
20 import android.car.annotation.FutureFeature;
21 import android.car.vms.IVmsSubscriberClient;
22 import android.car.vms.IVmsSubscriberService;
23 import android.car.vms.VmsLayer;
24 import android.content.Context;
25 import android.os.IBinder;
26 import android.os.RemoteException;
27 import android.util.Log;
28 
29 import com.android.car.hal.VmsHalService;
30 import com.android.internal.annotations.GuardedBy;
31 
32 import java.io.PrintWriter;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 
40 /**
41  * + Receives HAL updates by implementing VmsHalService.VmsHalListener.
42  * + Offers subscriber/publisher services by implementing IVmsService.Stub.
43  */
44 @FutureFeature
45 public class VmsSubscriberService extends IVmsSubscriberService.Stub
46         implements CarServiceBase, VmsHalService.VmsHalSubscriberListener {
47     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;
53 
54     @GuardedBy("mSubscriberServiceLock")
55     private final VmsListenerManager mMessageReceivedManager = new VmsListenerManager();
56     private final Object mSubscriberServiceLock = new Object();
57 
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          */
65         private final Object mListenerManagerLock = new Object();
66         @GuardedBy("mListenerManagerLock")
67         private final Map<IBinder, ListenerDeathRecipient> mListenerDeathRecipientMap =
68                 new HashMap<>();
69         @GuardedBy("mListenerManagerLock")
70         private final Map<IBinder, IVmsSubscriberClient> mListenerMap = new HashMap<>();
71 
72         class ListenerDeathRecipient implements IBinder.DeathRecipient {
73             private IBinder mListenerBinder;
74 
ListenerDeathRecipient(IBinder listenerBinder)75             ListenerDeathRecipient(IBinder listenerBinder) {
76                 mListenerBinder = listenerBinder;
77             }
78 
79             /**
80              * Listener died. Remove it from this service.
81              */
82             @Override
binderDied()83             public void binderDied() {
84                 if (DBG) {
85                     Log.d(TAG, "binderDied " + mListenerBinder);
86                 }
87 
88                 // Get the Listener from the Binder
89                 IVmsSubscriberClient listener = mListenerMap.get(mListenerBinder);
90 
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
101                 VmsListenerManager.this.removeListener(mListenerBinder);
102             }
103 
release()104             void release() {
105                 mListenerBinder.unlinkToDeath(this, 0);
106             }
107         }
108 
release()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          */
add(IVmsSubscriberClient listener)125         public void add(IVmsSubscriberClient listener) {
126             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();
135             synchronized (mListenerManagerLock) {
136                 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          */
remove(IVmsSubscriberClient listener)158         public void remove(IVmsSubscriberClient listener) {
159             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.
removeListener(IBinder listenerBinder)173         private void removeListener(IBinder listenerBinder) {
174             synchronized (mListenerManagerLock) {
175                 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          */
getListeners()190         public List<IVmsSubscriberClient> getListeners() {
191             synchronized (mListenerManagerLock) {
192                 return new ArrayList<>(mListenerMap.values());
193             }
194         }
195     }
196 
VmsSubscriberService(Context context, VmsHalService hal)197     public VmsSubscriberService(Context context, VmsHalService hal) {
198         mContext = context;
199         mHal = hal;
200     }
201 
202     // Implements CarServiceBase interface.
203     @Override
init()204     public void init() {
205         mHal.addSubscriberListener(this);
206     }
207 
208     @Override
release()209     public void release() {
210         mMessageReceivedManager.release();
211         mHal.removeSubscriberListener(this);
212     }
213 
214     @Override
dump(PrintWriter writer)215     public void dump(PrintWriter writer) {
216     }
217 
218     // Implements IVmsService interface.
219     @Override
addVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer)220     public void addVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) {
221         synchronized (mSubscriberServiceLock) {
222             // Add the listener so it can subscribe.
223             mMessageReceivedManager.add(listener);
224 
225             // Add the subscription for the layer.
226             mHal.addSubscription(listener, layer);
227         }
228     }
229 
230     @Override
removeVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer)231     public void removeVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) {
232         synchronized (mSubscriberServiceLock) {
233             // Remove the subscription.
234             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
addVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener)244     public void addVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) {
245         synchronized (mSubscriberServiceLock) {
246             mMessageReceivedManager.add(listener);
247             mHal.addSubscription(listener);
248         }
249     }
250 
251     @Override
removeVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener)252     public void removeVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) {
253         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         }
262     }
263 
264     @Override
getAvailableLayers()265     public List<VmsLayer> getAvailableLayers() {
266         //TODO(asafro): return the list of available layers once logic is implemented.
267         return Collections.emptyList();
268     }
269 
270     // Implements VmsHalSubscriberListener interface
271     @Override
onChange(VmsLayer layer, byte[] payload)272     public void onChange(VmsLayer layer, byte[] payload) {
273         if(DBG) {
274             Log.d(TAG, "Publishing a message for layer: " + layer);
275         }
276 
277         Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
278 
279         // If there are no listeners we're done.
280         if ((listeners == null)) {
281             return;
282         }
283 
284         for (IVmsSubscriberClient subscriber : listeners) {
285             try {
286                 subscriber.onVmsMessageReceived(layer, payload);
287             } 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         }
293 
294     }
295 }
296