• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.CallbackExecutor;
20 import android.annotation.NonNull;
21 import android.annotation.SystemApi;
22 import android.car.Car;
23 import android.car.CarManagerBase;
24 import android.car.CarNotConnectedException;
25 import android.car.vms.VmsSubscriberManager.VmsSubscriberClientCallback;
26 import android.os.Handler;
27 import android.os.Binder;
28 import android.os.IBinder;
29 import android.os.Looper;
30 import android.os.Message;
31 import android.os.RemoteException;
32 import android.util.Log;
33 
34 import com.android.internal.annotations.GuardedBy;
35 import com.android.internal.util.Preconditions;
36 
37 import java.lang.ref.WeakReference;
38 import java.util.concurrent.Executor;
39 
40 /**
41  * API for interfacing with the VmsSubscriberService. It supports a single client callback that can
42  * (un)subscribe to different layers. Getting notifactions and managing subscriptions is enabled
43  * after setting the client callback with #setVmsSubscriberClientCallback.
44  * SystemApi candidate
45  *
46  * @hide
47  */
48 @SystemApi
49 public final class VmsSubscriberManager implements CarManagerBase {
50     private static final boolean DBG = true;
51     private static final String TAG = "VmsSubscriberManager";
52 
53     private final IVmsSubscriberService mVmsSubscriberService;
54     private final IVmsSubscriberClient mSubscriberManagerClient;
55     private final Object mClientCallbackLock = new Object();
56     @GuardedBy("mClientCallbackLock")
57     private VmsSubscriberClientCallback mClientCallback;
58     @GuardedBy("mClientCallbackLock")
59     private Executor mExecutor;
60 
61     /**
62      * Interface exposed to VMS subscribers: it is a wrapper of IVmsSubscriberClient.
63      */
64     public interface VmsSubscriberClientCallback {
65         /**
66          * Called when the property is updated
67          */
onVmsMessageReceived(VmsLayer layer, byte[] payload)68         void onVmsMessageReceived(VmsLayer layer, byte[] payload);
69 
70         /**
71          * Called when layers availability change
72          */
onLayersAvailabilityChanged(VmsAvailableLayers availableLayers)73         void onLayersAvailabilityChanged(VmsAvailableLayers availableLayers);
74     }
75 
76     /**
77      * Hidden constructor - can only be used internally.
78      * @hide
79      */
VmsSubscriberManager(IBinder service)80     public VmsSubscriberManager(IBinder service) {
81         mVmsSubscriberService = IVmsSubscriberService.Stub.asInterface(service);
82         mSubscriberManagerClient = new IVmsSubscriberClient.Stub() {
83             @Override
84             public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
85                 Executor executor;
86                 synchronized (mClientCallbackLock) {
87                     executor = mExecutor;
88                 }
89                 if (executor == null) {
90                     if (DBG) {
91                         Log.d(TAG, "Executor is null in onVmsMessageReceived");
92                     }
93                     return;
94                 }
95                 Binder.clearCallingIdentity();
96                 executor.execute(() -> {dispatchOnReceiveMessage(layer, payload);});
97             }
98 
99             @Override
100             public void onLayersAvailabilityChanged(VmsAvailableLayers availableLayers) {
101                 Executor executor;
102                 synchronized (mClientCallbackLock) {
103                     executor = mExecutor;
104                 }
105                 if (executor == null) {
106                     if (DBG) {
107                         Log.d(TAG, "Executor is null in onLayersAvailabilityChanged");
108                     }
109                     return;
110                 }
111                 Binder.clearCallingIdentity();
112                 executor.execute(() -> {dispatchOnAvailabilityChangeMessage(availableLayers);});
113             }
114         };
115     }
116 
117     /**
118      * Sets the callback for the notification of onVmsMessageReceived events.
119      * @param executor {@link Executor} to handle the callbacks
120      * @param clientCallback subscriber callback that will handle onVmsMessageReceived events.
121      * @throws IllegalStateException if the client callback was already set.
122      */
setVmsSubscriberClientCallback(@onNull @allbackExecutor Executor executor, @NonNull VmsSubscriberClientCallback clientCallback)123     public void setVmsSubscriberClientCallback(@NonNull @CallbackExecutor Executor executor,
124                 @NonNull VmsSubscriberClientCallback clientCallback)
125           throws CarNotConnectedException {
126         Preconditions.checkNotNull(clientCallback);
127         Preconditions.checkNotNull(executor);
128         synchronized (mClientCallbackLock) {
129             if (mClientCallback != null) {
130                 throw new IllegalStateException("Client callback is already configured.");
131             }
132             mClientCallback = clientCallback;
133             mExecutor = executor;
134         }
135         try {
136             mVmsSubscriberService.addVmsSubscriberToNotifications(mSubscriberManagerClient);
137         } catch (RemoteException e) {
138             Log.e(TAG, "Could not connect: ", e);
139             throw new CarNotConnectedException(e);
140         }
141     }
142 
143 
144     /**
145      * Clears the client callback which disables communication with the client.
146      *
147      * @throws CarNotConnectedException
148      */
clearVmsSubscriberClientCallback()149     public void clearVmsSubscriberClientCallback() throws CarNotConnectedException {
150         synchronized (mClientCallbackLock) {
151             if (mExecutor == null) return;
152         }
153         try {
154             mVmsSubscriberService.removeVmsSubscriberToNotifications(mSubscriberManagerClient);
155         } catch (RemoteException e) {
156             Log.e(TAG, "Could not connect: ", e);
157             throw new CarNotConnectedException(e);
158         }
159 
160         synchronized (mClientCallbackLock) {
161             mClientCallback = null;
162             mExecutor = null;
163         }
164     }
165 
166     /**
167      * Returns a serialized publisher information for a publisher ID.
168      */
getPublisherInfo(int publisherId)169     public byte[] getPublisherInfo(int publisherId)
170             throws CarNotConnectedException, IllegalStateException {
171         try {
172             return mVmsSubscriberService.getPublisherInfo(publisherId);
173         } catch (RemoteException e) {
174             Log.e(TAG, "Could not connect: ", e);
175             throw new CarNotConnectedException(e);
176         } catch (IllegalStateException ex) {
177             Car.checkCarNotConnectedExceptionFromCarService(ex);
178             throw new IllegalStateException(ex);
179         }
180     }
181 
182     /**
183      * Returns the available layers.
184      */
getAvailableLayers()185     public VmsAvailableLayers getAvailableLayers()
186             throws CarNotConnectedException, IllegalStateException {
187         try {
188             return mVmsSubscriberService.getAvailableLayers();
189         } catch (RemoteException e) {
190             Log.e(TAG, "Could not connect: ", e);
191             throw new CarNotConnectedException(e);
192         } catch (IllegalStateException ex) {
193             Car.checkCarNotConnectedExceptionFromCarService(ex);
194             throw new IllegalStateException(ex);
195         }
196     }
197 
198     /**
199      * Subscribes to listen to the layer specified.
200      *
201      * @param layer the layer to subscribe to.
202      * @throws IllegalStateException if the client callback was not set via
203      *                               {@link #setVmsSubscriberClientCallback}.
204      */
subscribe(VmsLayer layer)205     public void subscribe(VmsLayer layer) throws CarNotConnectedException {
206         verifySubscriptionIsAllowed();
207         try {
208             mVmsSubscriberService.addVmsSubscriber(mSubscriberManagerClient, layer);
209             VmsOperationRecorder.get().subscribe(layer);
210         } catch (RemoteException e) {
211             Log.e(TAG, "Could not connect: ", e);
212             throw new CarNotConnectedException(e);
213         } catch (IllegalStateException ex) {
214             Car.checkCarNotConnectedExceptionFromCarService(ex);
215         }
216     }
217 
218     /**
219      * Subscribes to listen to the layer specified from the publisher specified.
220      *
221      * @param layer       the layer to subscribe to.
222      * @param publisherId the publisher of the layer.
223      * @throws IllegalStateException if the client callback was not set via
224      *                               {@link #setVmsSubscriberClientCallback}.
225      */
subscribe(VmsLayer layer, int publisherId)226     public void subscribe(VmsLayer layer, int publisherId) throws CarNotConnectedException {
227         verifySubscriptionIsAllowed();
228         try {
229             mVmsSubscriberService.addVmsSubscriberToPublisher(
230                     mSubscriberManagerClient, layer, publisherId);
231             VmsOperationRecorder.get().subscribe(layer, publisherId);
232         } catch (RemoteException e) {
233             Log.e(TAG, "Could not connect: ", e);
234             throw new CarNotConnectedException(e);
235         } catch (IllegalStateException ex) {
236             Car.checkCarNotConnectedExceptionFromCarService(ex);
237         }
238     }
239 
startMonitoring()240     public void startMonitoring() throws CarNotConnectedException {
241         verifySubscriptionIsAllowed();
242         try {
243             mVmsSubscriberService.addVmsSubscriberPassive(mSubscriberManagerClient);
244             VmsOperationRecorder.get().startMonitoring();
245         } catch (RemoteException e) {
246             Log.e(TAG, "Could not connect: ", e);
247             throw new CarNotConnectedException(e);
248         } catch (IllegalStateException ex) {
249             Car.checkCarNotConnectedExceptionFromCarService(ex);
250         }
251     }
252 
253     /**
254      * Unsubscribes from the layer/version specified.
255      *
256      * @param layer the layer to unsubscribe from.
257      * @throws IllegalStateException if the client callback was not set via
258      *                               {@link #setVmsSubscriberClientCallback}.
259      */
unsubscribe(VmsLayer layer)260     public void unsubscribe(VmsLayer layer) {
261         verifySubscriptionIsAllowed();
262         try {
263             mVmsSubscriberService.removeVmsSubscriber(mSubscriberManagerClient, layer);
264             VmsOperationRecorder.get().unsubscribe(layer);
265         } catch (RemoteException e) {
266             Log.e(TAG, "Failed to clear subscriber", e);
267             // ignore
268         } catch (IllegalStateException ex) {
269             Car.hideCarNotConnectedExceptionFromCarService(ex);
270         }
271     }
272 
273     /**
274      * Unsubscribes from the layer/version specified.
275      *
276      * @param layer       the layer to unsubscribe from.
277      * @param publisherId the pubisher of the layer.
278      * @throws IllegalStateException if the client callback was not set via
279      *                               {@link #setVmsSubscriberClientCallback}.
280      */
unsubscribe(VmsLayer layer, int publisherId)281     public void unsubscribe(VmsLayer layer, int publisherId) {
282         try {
283             mVmsSubscriberService.removeVmsSubscriberToPublisher(
284                     mSubscriberManagerClient, layer, publisherId);
285             VmsOperationRecorder.get().unsubscribe(layer, publisherId);
286         } catch (RemoteException e) {
287             Log.e(TAG, "Failed to clear subscriber", e);
288             // ignore
289         } catch (IllegalStateException ex) {
290             Car.hideCarNotConnectedExceptionFromCarService(ex);
291         }
292     }
293 
stopMonitoring()294     public void stopMonitoring() {
295         try {
296             mVmsSubscriberService.removeVmsSubscriberPassive(mSubscriberManagerClient);
297             VmsOperationRecorder.get().stopMonitoring();
298         } catch (RemoteException e) {
299             Log.e(TAG, "Failed to clear subscriber ", e);
300             // ignore
301         } catch (IllegalStateException ex) {
302             Car.hideCarNotConnectedExceptionFromCarService(ex);
303         }
304     }
305 
dispatchOnReceiveMessage(VmsLayer layer, byte[] payload)306     private void dispatchOnReceiveMessage(VmsLayer layer, byte[] payload) {
307         VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
308         if (clientCallback == null) {
309             Log.e(TAG, "Cannot dispatch received message.");
310             return;
311         }
312         clientCallback.onVmsMessageReceived(layer, payload);
313     }
314 
dispatchOnAvailabilityChangeMessage(VmsAvailableLayers availableLayers)315     private void dispatchOnAvailabilityChangeMessage(VmsAvailableLayers availableLayers) {
316         VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
317         if (clientCallback == null) {
318             Log.e(TAG, "Cannot dispatch availability change message.");
319             return;
320         }
321         clientCallback.onLayersAvailabilityChanged(availableLayers);
322     }
323 
getClientCallbackThreadSafe()324     private VmsSubscriberClientCallback getClientCallbackThreadSafe() {
325         VmsSubscriberClientCallback clientCallback;
326         synchronized (mClientCallbackLock) {
327             clientCallback = mClientCallback;
328         }
329         if (clientCallback == null) {
330             Log.e(TAG, "client callback not set.");
331         }
332         return clientCallback;
333     }
334 
335     /*
336      * Verifies that the subscriber is in a state where it is allowed to subscribe.
337      */
verifySubscriptionIsAllowed()338     private void verifySubscriptionIsAllowed() {
339         VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
340         if (clientCallback == null) {
341             throw new IllegalStateException("Cannot subscribe.");
342         }
343     }
344 
345     /**
346      * @hide
347      */
348     @Override
onCarDisconnected()349     public void onCarDisconnected() {
350     }
351 
352     private static final class VmsDataMessage {
353         private final VmsLayer mLayer;
354         private final byte[] mPayload;
355 
VmsDataMessage(VmsLayer layer, byte[] payload)356         public VmsDataMessage(VmsLayer layer, byte[] payload) {
357             mLayer = layer;
358             mPayload = payload;
359         }
360 
getLayer()361         public VmsLayer getLayer() {
362             return mLayer;
363         }
364 
getPayload()365         public byte[] getPayload() {
366             return mPayload;
367         }
368     }
369 }
370