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 package com.android.car.hal;
17 
18 import static com.android.car.CarServiceUtils.toByteArray;
19 import static java.lang.Integer.toHexString;
20 
21 import android.car.VehicleAreaType;
22 import android.car.annotation.FutureFeature;
23 import android.car.vms.IVmsSubscriberClient;
24 import android.car.vms.VmsLayer;
25 import android.car.vms.VmsSubscriptionState;
26 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
27 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
28 import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
29 import android.hardware.automotive.vehicle.V2_1.VmsMessageIntegerValuesIndex;
30 import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
31 import android.os.SystemClock;
32 import android.util.Log;
33 import com.android.car.CarLog;
34 import com.android.car.VmsRouting;
35 import com.android.internal.annotations.GuardedBy;
36 import java.io.PrintWriter;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.HashSet;
41 import java.util.LinkedList;
42 import java.util.List;
43 import java.util.Set;
44 import java.util.concurrent.CopyOnWriteArrayList;
45 
46 /**
47  * This is a glue layer between the VehicleHal and the VmsService. It sends VMS properties back and
48  * forth.
49  */
50 @FutureFeature
51 public class VmsHalService extends HalServiceBase {
52     private static final boolean DBG = true;
53     private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE;
54     private static final String TAG = "VmsHalService";
55     private static final Set<Integer> SUPPORTED_MESSAGE_TYPES =
56         new HashSet<Integer>(
57             Arrays.asList(
58                 VmsMessageType.SUBSCRIBE,
59                 VmsMessageType.UNSUBSCRIBE,
60                 VmsMessageType.DATA));
61 
62     private boolean mIsSupported = false;
63     private CopyOnWriteArrayList<VmsHalPublisherListener> mPublisherListeners =
64         new CopyOnWriteArrayList<>();
65     private CopyOnWriteArrayList<VmsHalSubscriberListener> mSubscriberListeners =
66         new CopyOnWriteArrayList<>();
67     private final VehicleHal mVehicleHal;
68     @GuardedBy("mLock")
69     private VmsRouting mRouting = new VmsRouting();
70     private final Object mLock = new Object();
71 
72     /**
73      * The VmsPublisherService implements this interface to receive data from the HAL.
74      */
75     public interface VmsHalPublisherListener {
onChange(VmsSubscriptionState subscriptionState)76         void onChange(VmsSubscriptionState subscriptionState);
77     }
78 
79     /**
80      * The VmsSubscriberService implements this interface to receive data from the HAL.
81      */
82     public interface VmsHalSubscriberListener {
onChange(VmsLayer layer, byte[] payload)83         void onChange(VmsLayer layer, byte[] payload);
84     }
85 
86     /**
87      * The VmsService implements this interface to receive data from the HAL.
88      */
VmsHalService(VehicleHal vehicleHal)89     protected VmsHalService(VehicleHal vehicleHal) {
90         mVehicleHal = vehicleHal;
91         if (DBG) {
92             Log.d(TAG, "started VmsHalService!");
93         }
94     }
95 
addPublisherListener(VmsHalPublisherListener listener)96     public void addPublisherListener(VmsHalPublisherListener listener) {
97         mPublisherListeners.add(listener);
98     }
99 
addSubscriberListener(VmsHalSubscriberListener listener)100     public void addSubscriberListener(VmsHalSubscriberListener listener) {
101         mSubscriberListeners.add(listener);
102     }
103 
removePublisherListener(VmsHalPublisherListener listener)104     public void removePublisherListener(VmsHalPublisherListener listener) {
105         mPublisherListeners.remove(listener);
106     }
107 
removeSubscriberListener(VmsHalSubscriberListener listener)108     public void removeSubscriberListener(VmsHalSubscriberListener listener) {
109         mSubscriberListeners.remove(listener);
110     }
111 
addSubscription(IVmsSubscriberClient listener, VmsLayer layer)112     public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
113         synchronized (mLock) {
114             // Check if publishers need to be notified about this change in subscriptions.
115             boolean firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
116 
117             // Add the listeners subscription to the layer
118             mRouting.addSubscription(listener, layer);
119 
120             // Notify the publishers
121             if (firstSubscriptionForLayer) {
122                 notifyPublishers(layer, true);
123             }
124         }
125     }
126 
removeSubscription(IVmsSubscriberClient listener, VmsLayer layer)127     public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
128         synchronized (mLock) {
129             if (!mRouting.hasLayerSubscriptions(layer)) {
130                 Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
131                 return;
132             }
133 
134             // Remove the listeners subscription to the layer
135             mRouting.removeSubscription(listener, layer);
136 
137             // Check if publishers need to be notified about this change in subscriptions.
138             boolean layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
139 
140             // Notify the publishers
141             if (!layerHasSubscribers) {
142                 notifyPublishers(layer, false);
143             }
144         }
145     }
146 
addSubscription(IVmsSubscriberClient listener)147     public void addSubscription(IVmsSubscriberClient listener) {
148         synchronized (mLock) {
149             mRouting.addSubscription(listener);
150         }
151     }
152 
removeSubscription(IVmsSubscriberClient listener)153     public void removeSubscription(IVmsSubscriberClient listener) {
154         synchronized (mLock) {
155             mRouting.removeSubscription(listener);
156         }
157     }
158 
removeDeadListener(IVmsSubscriberClient listener)159     public void removeDeadListener(IVmsSubscriberClient listener) {
160         synchronized (mLock) {
161             mRouting.removeDeadListener(listener);
162         }
163     }
164 
getListeners(VmsLayer layer)165     public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) {
166         synchronized (mLock) {
167             return mRouting.getListeners(layer);
168         }
169     }
170 
isHalSubscribed(VmsLayer layer)171     public boolean isHalSubscribed(VmsLayer layer) {
172         synchronized (mLock) {
173             return mRouting.isHalSubscribed(layer);
174         }
175     }
176 
getSubscriptionState()177     public VmsSubscriptionState getSubscriptionState() {
178         synchronized (mLock) {
179             return mRouting.getSubscriptionState();
180         }
181     }
182 
addHalSubscription(VmsLayer layer)183     public void addHalSubscription(VmsLayer layer) {
184         synchronized (mLock) {
185             // Check if publishers need to be notified about this change in subscriptions.
186             boolean firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
187 
188             // Add the listeners subscription to the layer
189             mRouting.addHalSubscription(layer);
190 
191             if (firstSubscriptionForLayer) {
192                 notifyPublishers(layer, true);
193             }
194         }
195     }
196 
removeHalSubscription(VmsLayer layer)197     public void removeHalSubscription(VmsLayer layer) {
198         synchronized (mLock) {
199             if (!mRouting.hasLayerSubscriptions(layer)) {
200                 Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
201                 return;
202             }
203 
204             // Remove the listeners subscription to the layer
205             mRouting.removeHalSubscription(layer);
206 
207             // Check if publishers need to be notified about this change in subscriptions.
208             boolean layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
209 
210             // Notify the publishers
211             if (!layerHasSubscribers) {
212                 notifyPublishers(layer, false);
213             }
214         }
215     }
216 
containsListener(IVmsSubscriberClient listener)217     public boolean containsListener(IVmsSubscriberClient listener) {
218         synchronized (mLock) {
219             return mRouting.containsListener(listener);
220         }
221     }
222 
223     /**
224      * Notify all the publishers and the HAL on subscription changes regardless of who triggered
225      * the change.
226      *
227      * @param layer          layer which is being subscribed to or unsubscribed from.
228      * @param hasSubscribers indicates if the notification is for subscription or unsubscription.
229      */
notifyPublishers(VmsLayer layer, boolean hasSubscribers)230     public void notifyPublishers(VmsLayer layer, boolean hasSubscribers) {
231         synchronized (mLock) {
232             // notify the HAL
233             setSubscriptionRequest(layer, hasSubscribers);
234 
235             // Notify the App publishers
236             for (VmsHalPublisherListener listener : mPublisherListeners) {
237                 // Besides the list of layers, also a timestamp is provided to the clients.
238                 // They should ignore any notification with a timestamp that is older than the most
239                 // recent timestamp they have seen.
240                 listener.onChange(getSubscriptionState());
241             }
242         }
243     }
244 
245     @Override
init()246     public void init() {
247         if (DBG) {
248             Log.d(TAG, "init()");
249         }
250         if (mIsSupported) {
251             mVehicleHal.subscribeProperty(this, HAL_PROPERTY_ID);
252         }
253     }
254 
255     @Override
release()256     public void release() {
257         if (DBG) {
258             Log.d(TAG, "release()");
259         }
260         if (mIsSupported) {
261             mVehicleHal.unsubscribeProperty(this, HAL_PROPERTY_ID);
262         }
263         mPublisherListeners.clear();
264         mSubscriberListeners.clear();
265     }
266 
267     @Override
takeSupportedProperties( Collection<VehiclePropConfig> allProperties)268     public Collection<VehiclePropConfig> takeSupportedProperties(
269             Collection<VehiclePropConfig> allProperties) {
270         List<VehiclePropConfig> taken = new LinkedList<>();
271         for (VehiclePropConfig p : allProperties) {
272             if (p.prop == HAL_PROPERTY_ID) {
273                 taken.add(p);
274                 mIsSupported = true;
275                 if (DBG) {
276                     Log.d(TAG, "takeSupportedProperties: " + toHexString(p.prop));
277                 }
278                 break;
279             }
280         }
281         return taken;
282     }
283 
284     @Override
handleHalEvents(List<VehiclePropValue> values)285     public void handleHalEvents(List<VehiclePropValue> values) {
286         if (DBG) {
287             Log.d(TAG, "Handling a VMS property change");
288         }
289         for (VehiclePropValue v : values) {
290             ArrayList<Integer> vec = v.value.int32Values;
291             int messageType = vec.get(VmsMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
292             int layerId = vec.get(VmsMessageIntegerValuesIndex.VMS_LAYER_ID);
293             int layerVersion = vec.get(VmsMessageIntegerValuesIndex.VMS_LAYER_VERSION);
294 
295             // Check if message type is supported.
296             if (!SUPPORTED_MESSAGE_TYPES.contains(messageType)) {
297                 throw new IllegalArgumentException("Unexpected message type. " +
298                     "Expecting: " + SUPPORTED_MESSAGE_TYPES +
299                     ". Got: " + messageType);
300 
301             }
302 
303             if (DBG) {
304                 Log.d(TAG,
305                     "Received message for Type: " + messageType +
306                         " Layer Id: " + layerId +
307                         "Version: " + layerVersion);
308             }
309             // This is a data message intended for subscribers.
310             if (messageType == VmsMessageType.DATA) {
311                 // Get the payload.
312                 byte[] payload = toByteArray(v.value.bytes);
313 
314                 // Send the message.
315                 for (VmsHalSubscriberListener listener : mSubscriberListeners) {
316                     listener.onChange(new VmsLayer(layerId, layerVersion), payload);
317                 }
318             } else if (messageType == VmsMessageType.SUBSCRIBE) {
319                 addHalSubscription(new VmsLayer(layerId, layerVersion));
320             } else {
321                 // messageType == VmsMessageType.UNSUBSCRIBE
322                 removeHalSubscription(new VmsLayer(layerId, layerVersion));
323             }
324         }
325     }
326 
327     @Override
dump(PrintWriter writer)328     public void dump(PrintWriter writer) {
329         writer.println(TAG);
330         writer.println("VmsProperty " + (mIsSupported ? "" : "not") + " supported.");
331     }
332 
333     /**
334      * Updates the VMS HAL property with the given value.
335      *
336      * @param layer          layer data to update the hal property.
337      * @param hasSubscribers if it is a subscribe or unsubscribe message.
338      * @return true if the call to the HAL to update the property was successful.
339      */
setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers)340     public boolean setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers) {
341         VehiclePropValue vehiclePropertyValue = toVehiclePropValue(
342                 hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer);
343         return setPropertyValue(vehiclePropertyValue);
344     }
345 
setDataMessage(VmsLayer layer, byte[] payload)346     public boolean setDataMessage(VmsLayer layer, byte[] payload) {
347         VehiclePropValue vehiclePropertyValue = toVehiclePropValue(VmsMessageType.DATA,
348                 layer,
349                 payload);
350         return setPropertyValue(vehiclePropertyValue);
351     }
352 
setPropertyValue(VehiclePropValue vehiclePropertyValue)353     public boolean setPropertyValue(VehiclePropValue vehiclePropertyValue) {
354         try {
355             mVehicleHal.set(vehiclePropertyValue);
356             return true;
357         } catch (PropertyTimeoutException e) {
358             Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(HAL_PROPERTY_ID));
359         }
360         return false;
361     }
362 
363     /** Creates a {@link VehiclePropValue} */
toVehiclePropValue(int messageType, VmsLayer layer)364     private static VehiclePropValue toVehiclePropValue(int messageType, VmsLayer layer) {
365         VehiclePropValue vehicleProp = new VehiclePropValue();
366         vehicleProp.prop = HAL_PROPERTY_ID;
367         vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_NONE;
368         VehiclePropValue.RawValue v = vehicleProp.value;
369 
370         v.int32Values.add(messageType);
371         v.int32Values.add(layer.getId());
372         v.int32Values.add(layer.getVersion());
373         return vehicleProp;
374     }
375 
376     /** Creates a {@link VehiclePropValue} with payload */
toVehiclePropValue(int messageType, VmsLayer layer, byte[] payload)377     private static VehiclePropValue toVehiclePropValue(int messageType,
378             VmsLayer layer,
379             byte[] payload) {
380         VehiclePropValue vehicleProp = toVehiclePropValue(messageType, layer);
381         VehiclePropValue.RawValue v = vehicleProp.value;
382         v.bytes.ensureCapacity(payload.length);
383         for (byte b : payload) {
384             v.bytes.add(b);
385         }
386         return vehicleProp;
387     }
388 }