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 android.car.vms;
18 
19 import static com.android.car.internal.common.CommonConstants.EMPTY_BYTE_ARRAY;
20 
21 import android.annotation.CallbackExecutor;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SystemApi;
25 import android.car.Car;
26 import android.car.CarManagerBase;
27 import android.car.annotation.RequiredFeature;
28 import android.car.vms.VmsClientManager.VmsClientCallback;
29 
30 import com.android.internal.annotations.GuardedBy;
31 
32 import java.util.Objects;
33 import java.util.Set;
34 import java.util.concurrent.CountDownLatch;
35 import java.util.concurrent.Executor;
36 import java.util.concurrent.TimeUnit;
37 
38 /**
39  * API implementation for use by Vehicle Map Service subscribers.
40  *
41  * Supports a single client callback that can subscribe and unsubscribe to different data layers.
42  * {@link #setVmsSubscriberClientCallback} must be called before any subscription operations.
43  *
44  * @deprecated Use {@link VmsClientManager} instead
45  * @hide
46  */
47 @RequiredFeature(Car.VMS_SUBSCRIBER_SERVICE)
48 @Deprecated
49 @SystemApi
50 public final class VmsSubscriberManager extends CarManagerBase {
51     private static final long CLIENT_READY_TIMEOUT_MS = 500;
52     private static final byte[] DEFAULT_PUBLISHER_INFO = EMPTY_BYTE_ARRAY;
53 
54     /**
55      * Callback interface for Vehicle Map Service subscribers.
56      */
57     public interface VmsSubscriberClientCallback {
58         /**
59          * Called when a data packet is received.
60          *
61          * @param layer   subscribed layer that packet was received for
62          * @param payload data packet that was received
63          */
onVmsMessageReceived(@onNull VmsLayer layer, byte[] payload)64         void onVmsMessageReceived(@NonNull VmsLayer layer, byte[] payload);
65 
66         /**
67          * Called when set of available data layers changes.
68          *
69          * @param availableLayers set of available data layers
70          */
onLayersAvailabilityChanged(@onNull VmsAvailableLayers availableLayers)71         void onLayersAvailabilityChanged(@NonNull VmsAvailableLayers availableLayers);
72     }
73 
74     private final VmsClientManager mClientManager;
75 
76     private final Object mLock = new Object();
77 
78     @GuardedBy("mLock")
79     private @Nullable VmsClient mClient;
80 
81     @GuardedBy("mLock")
82     private @Nullable VmsClientCallback mClientCallback;
83 
84     private final VmsSubscriptionHelper mSubscriptionHelper =
85             new VmsSubscriptionHelper(this::setSubscriptions);
86 
87     /**
88      * @hide
89      */
wrap(Car car, @Nullable VmsClientManager clientManager)90     public static VmsSubscriberManager wrap(Car car, @Nullable VmsClientManager clientManager) {
91         if (clientManager == null) {
92             return null;
93         }
94         return new VmsSubscriberManager(car, clientManager);
95     }
96 
VmsSubscriberManager(Car car, VmsClientManager clientManager)97     private VmsSubscriberManager(Car car, VmsClientManager clientManager) {
98         super(car);
99         mClientManager = clientManager;
100     }
101 
102     /**
103      * Sets the subscriber client's callback, for receiving layer availability and data events.
104      *
105      * @param executor       {@link Executor} to handle the callbacks
106      * @param clientCallback subscriber callback that will handle events
107      * @throws IllegalStateException if the client callback was already set
108      */
setVmsSubscriberClientCallback( @onNull @allbackExecutor Executor executor, @NonNull VmsSubscriberClientCallback clientCallback)109     public void setVmsSubscriberClientCallback(
110             @NonNull @CallbackExecutor Executor executor,
111             @NonNull VmsSubscriberClientCallback clientCallback) {
112         Objects.requireNonNull(clientCallback, "clientCallback cannot be null");
113         Objects.requireNonNull(executor, "executor cannot be null");
114         CountDownLatch clientReady;
115         synchronized (mLock) {
116             if (mClientCallback != null) {
117                 throw new IllegalStateException("Client callback is already configured.");
118             }
119             clientReady = new CountDownLatch(1);
120             mClientCallback = new SubscriberCallbackWrapper(clientCallback, clientReady);
121             // Register callback with broker service
122             mClientManager.registerVmsClientCallback(executor, mClientCallback,
123                     /* legacyClient= */ true);
124         }
125 
126         try {
127             // Wait for VmsClient to be available
128             if (!clientReady.await(CLIENT_READY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
129                 clearVmsSubscriberClientCallback();
130                 throw new IllegalStateException("Subscriber client is not ready");
131             }
132         } catch (InterruptedException e) {
133             clearVmsSubscriberClientCallback();
134             Thread.currentThread().interrupt();
135             throw new IllegalStateException("Interrupted while waiting for subscriber client", e);
136         }
137     }
138 
139     /**
140      * Clears the subscriber client's callback.
141      */
clearVmsSubscriberClientCallback()142     public void clearVmsSubscriberClientCallback() {
143         synchronized (mLock) {
144             mClientManager.unregisterVmsClientCallback(mClientCallback);
145             mClient = null;
146             mClientCallback = null;
147         }
148     }
149 
150     /**
151      * Gets a publisher's self-reported description information.
152      *
153      * @param publisherId publisher ID to retrieve information for
154      * @return serialized publisher information, in a vendor-specific format
155      */
156     @NonNull
getPublisherInfo(int publisherId)157     public byte[] getPublisherInfo(int publisherId) {
158         byte[] publisherInfo = getVmsClient().getProviderDescription(publisherId);
159         return publisherInfo != null ? publisherInfo : DEFAULT_PUBLISHER_INFO;
160     }
161 
162     /**
163      * Gets all layers available for subscription.
164      *
165      * @return available layers
166      */
167     @NonNull
getAvailableLayers()168     public VmsAvailableLayers getAvailableLayers() {
169         return getVmsClient().getAvailableLayers();
170     }
171 
172     /**
173      * Subscribes to data packets for a specific layer.
174      *
175      * @param layer layer to subscribe to
176      * @throws IllegalStateException if the client callback was not set via
177      *                               {@link #setVmsSubscriberClientCallback}.
178      */
subscribe(@onNull VmsLayer layer)179     public void subscribe(@NonNull VmsLayer layer) {
180         mSubscriptionHelper.subscribe(layer);
181     }
182 
183     /**
184      * Subscribes to data packets for a specific layer from a specific publisher.
185      *
186      * @param layer       layer to subscribe to
187      * @param publisherId a publisher of the layer
188      * @throws IllegalStateException if the client callback was not set via
189      *                               {@link #setVmsSubscriberClientCallback}.
190      */
subscribe(@onNull VmsLayer layer, int publisherId)191     public void subscribe(@NonNull VmsLayer layer, int publisherId) {
192         mSubscriptionHelper.subscribe(layer, publisherId);
193     }
194 
195     /**
196      * Start monitoring all messages for all layers, regardless of subscriptions.
197      */
startMonitoring()198     public void startMonitoring() {
199         getVmsClient().setMonitoringEnabled(true);
200     }
201 
202     /**
203      * Unsubscribes from data packets for a specific layer.
204      *
205      * @param layer layer to unsubscribe from
206      * @throws IllegalStateException if the client callback was not set via
207      *                               {@link #setVmsSubscriberClientCallback}.
208      */
unsubscribe(@onNull VmsLayer layer)209     public void unsubscribe(@NonNull VmsLayer layer) {
210         mSubscriptionHelper.unsubscribe(layer);
211     }
212 
213     /**
214      * Unsubscribes from data packets for a specific layer from a specific publisher.
215      *
216      * @param layer       layer to unsubscribe from
217      * @param publisherId a publisher of the layer
218      * @throws IllegalStateException if the client callback was not set via
219      *                               {@link #setVmsSubscriberClientCallback}.
220      */
unsubscribe(@onNull VmsLayer layer, int publisherId)221     public void unsubscribe(@NonNull VmsLayer layer, int publisherId) {
222         mSubscriptionHelper.unsubscribe(layer, publisherId);
223     }
224 
225     /**
226      * Stop monitoring. Only receive messages for layers which have been subscribed to."
227      */
stopMonitoring()228     public void stopMonitoring() {
229         getVmsClient().setMonitoringEnabled(false);
230     }
231 
232     /**
233      * @hide
234      */
235     @Override
onCarDisconnected()236     public void onCarDisconnected() {}
237 
setSubscriptions(Set<VmsAssociatedLayer> subscriptions)238     private void setSubscriptions(Set<VmsAssociatedLayer> subscriptions) {
239         getVmsClient().setSubscriptions(subscriptions);
240     }
241 
getVmsClient()242     private VmsClient getVmsClient() {
243         synchronized (mLock) {
244             if (mClient == null) {
245                 throw new IllegalStateException("VMS client connection is not ready");
246             }
247             return mClient;
248         }
249     }
250 
251     private final class SubscriberCallbackWrapper implements VmsClientCallback {
252         private final VmsSubscriberClientCallback mCallback;
253         private final CountDownLatch mClientReady;
254 
SubscriberCallbackWrapper(VmsSubscriberClientCallback callback, CountDownLatch clientReady)255         SubscriberCallbackWrapper(VmsSubscriberClientCallback callback,
256                 CountDownLatch clientReady) {
257             mCallback = callback;
258             mClientReady = clientReady;
259         }
260 
261         @Override
onClientConnected(VmsClient client)262         public void onClientConnected(VmsClient client) {
263             synchronized (mLock) {
264                 mClient = client;
265             }
266             mClientReady.countDown();
267         }
268 
269         @Override
onSubscriptionStateChanged(VmsSubscriptionState subscriptionState)270         public void onSubscriptionStateChanged(VmsSubscriptionState subscriptionState) {
271             // Ignored
272         }
273 
274         @Override
onLayerAvailabilityChanged(VmsAvailableLayers availableLayers)275         public void onLayerAvailabilityChanged(VmsAvailableLayers availableLayers) {
276             mCallback.onLayersAvailabilityChanged(availableLayers);
277         }
278 
279         @Override
onPacketReceived(int providerId, VmsLayer layer, byte[] packet)280         public void onPacketReceived(int providerId, VmsLayer layer, byte[] packet) {
281             mCallback.onVmsMessageReceived(layer, packet);
282         }
283     }
284 }
285