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.car.Car;
20 import android.car.CarManagerBase;
21 import android.car.CarNotConnectedException;
22 import android.car.annotation.FutureFeature;
23 import android.os.Handler;
24 import android.os.IBinder;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.RemoteException;
28 import android.util.Log;
29 import com.android.internal.annotations.GuardedBy;
30 import java.lang.ref.WeakReference;
31 import java.util.List;
32 
33 /**
34  * API for interfacing with the VmsSubscriberService. It supports a single listener that can
35  * (un)subscribe to different layers. After getting an instance of this manager, the first step
36  * must be to call #setListener. After that, #subscribe and #unsubscribe methods can be invoked.
37  * SystemApi candidate
38  *
39  * @hide
40  */
41 @FutureFeature
42 public final class VmsSubscriberManager implements CarManagerBase {
43     private static final boolean DBG = true;
44     private static final String TAG = "VmsSubscriberManager";
45 
46     private final Handler mHandler;
47     private final IVmsSubscriberService mVmsSubscriberService;
48     private final IVmsSubscriberClient mIListener;
49     private final Object mListenerLock = new Object();
50     @GuardedBy("mListenerLock")
51     private VmsSubscriberClientListener mListener;
52 
53     /** Interface exposed to VMS subscribers: it is a wrapper of IVmsSubscriberClient. */
54     public interface VmsSubscriberClientListener {
55         /** Called when the property is updated */
onVmsMessageReceived(VmsLayer layer, byte[] payload)56         void onVmsMessageReceived(VmsLayer layer, byte[] payload);
57 
58         /** Called when layers availability change */
onLayersAvailabilityChange(List<VmsLayer> availableLayers)59         void onLayersAvailabilityChange(List<VmsLayer> availableLayers);
60     }
61 
62     /**
63      * Allows to asynchronously dispatch onVmsMessageReceived events.
64      */
65     private final static class VmsEventHandler extends Handler {
66         /** Constants handled in the handler */
67         private static final int ON_RECEIVE_MESSAGE_EVENT = 0;
68         private static final int ON_AVAILABILITY_CHANGE_EVENT = 1;
69 
70         private final WeakReference<VmsSubscriberManager> mMgr;
71 
VmsEventHandler(VmsSubscriberManager mgr, Looper looper)72         VmsEventHandler(VmsSubscriberManager mgr, Looper looper) {
73             super(looper);
74             mMgr = new WeakReference<>(mgr);
75         }
76 
77         @Override
handleMessage(Message msg)78         public void handleMessage(Message msg) {
79             VmsSubscriberManager mgr = mMgr.get();
80             switch (msg.what) {
81                 case ON_RECEIVE_MESSAGE_EVENT:
82                     if (mgr != null) {
83                         // Parse the message
84                         VmsDataMessage vmsDataMessage = (VmsDataMessage) msg.obj;
85 
86                         // Dispatch the parsed message
87                         mgr.dispatchOnReceiveMessage(vmsDataMessage.getLayer(),
88                                                      vmsDataMessage.getPayload());
89                     }
90                     break;
91                 case ON_AVAILABILITY_CHANGE_EVENT:
92                     if (mgr != null) {
93                         // Parse the message
94                         List<VmsLayer> vmsAvailabilityChangeMessage = (List<VmsLayer>) msg.obj;
95 
96                         // Dispatch the parsed message
97                         mgr.dispatchOnAvailabilityChangeMessage(vmsAvailabilityChangeMessage);
98                     }
99                     break;
100 
101                 default:
102                     Log.e(VmsSubscriberManager.TAG, "Event type not handled:  " + msg.what);
103                     break;
104             }
105         }
106     }
107 
VmsSubscriberManager(IBinder service, Handler handler)108     public VmsSubscriberManager(IBinder service, Handler handler) {
109         mVmsSubscriberService = IVmsSubscriberService.Stub.asInterface(service);
110         mHandler = new VmsEventHandler(this, handler.getLooper());
111         mIListener = new IVmsSubscriberClient.Stub() {
112             @Override
113             public void onVmsMessageReceived(VmsLayer layer, byte[] payload)
114                 throws RemoteException {
115                 // Create the data message
116                 VmsDataMessage vmsDataMessage = new VmsDataMessage(layer, payload);
117                 mHandler.sendMessage(
118                         mHandler.obtainMessage(
119                             VmsEventHandler.ON_RECEIVE_MESSAGE_EVENT,
120                             vmsDataMessage));
121             }
122 
123             @Override
124             public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
125                 mHandler.sendMessage(
126                     mHandler.obtainMessage(
127                         VmsEventHandler.ON_AVAILABILITY_CHANGE_EVENT,
128                         availableLayers));
129             }
130         };
131     }
132 
133     /**
134      * Sets the listener ({@link #mListener}) this manager is linked to. Subscriptions to the
135      * {@link com.android.car.VmsSubscriberService} are done through the {@link #mIListener}.
136      * Therefore, notifications from the {@link com.android.car.VmsSubscriberService} are received
137      * by the {@link #mIListener} and then forwarded to the {@link #mListener}.
138      *
139      * @param listener subscriber listener that will handle onVmsMessageReceived events.
140      * @throws IllegalStateException if the listener was already set.
141      */
setListener(VmsSubscriberClientListener listener)142     public void setListener(VmsSubscriberClientListener listener) {
143         if (DBG) {
144             Log.d(TAG, "Setting listener.");
145         }
146         synchronized (mListenerLock) {
147             if (mListener != null) {
148                 throw new IllegalStateException("Listener is already configured.");
149             }
150             mListener = listener;
151         }
152     }
153 
154     /**
155      * Removes the listener and unsubscribes from all the layer/version.
156      */
clearListener()157     public void clearListener() {
158         synchronized (mListenerLock) {
159             mListener = null;
160         }
161         // TODO(antoniocortes): logic to unsubscribe from all the layer/version pairs.
162     }
163 
164     /**
165      * Subscribes to listen to the layer specified.
166      *
167      * @param layer the layer to subscribe to.
168      * @throws IllegalStateException if the listener was not set via {@link #setListener}.
169      */
subscribe(VmsLayer layer)170     public void subscribe(VmsLayer layer)
171             throws CarNotConnectedException {
172         if (DBG) {
173             Log.d(TAG, "Subscribing to layer: " + layer);
174         }
175         VmsSubscriberClientListener listener;
176         synchronized (mListenerLock) {
177             listener = mListener;
178         }
179         if (listener == null) {
180             Log.w(TAG, "subscribe: listener was not set, " +
181                     "setListener must be called first.");
182             throw new IllegalStateException("Listener was not set.");
183         }
184         try {
185             mVmsSubscriberService.addVmsSubscriberClientListener(mIListener, layer);
186         } catch (RemoteException e) {
187             Log.e(TAG, "Could not connect: ", e);
188             throw new CarNotConnectedException(e);
189         } catch (IllegalStateException ex) {
190             Car.checkCarNotConnectedExceptionFromCarService(ex);
191         }
192     }
193 
subscribeAll()194     public void subscribeAll()
195         throws CarNotConnectedException {
196         if (DBG) {
197             Log.d(TAG, "Subscribing passively to all data messages");
198         }
199         VmsSubscriberClientListener listener;
200         synchronized (mListenerLock) {
201             listener = mListener;
202         }
203         if (listener == null) {
204             Log.w(TAG, "subscribe: listener was not set, " +
205                 "setListener must be called first.");
206             throw new IllegalStateException("Listener was not set.");
207         }
208         try {
209             mVmsSubscriberService.addVmsSubscriberClientPassiveListener(mIListener);
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      * Unsubscribes from the layer/version specified.
220      *
221      * @param layer   the layer to unsubscribe from.
222      * @throws IllegalStateException if the listener was not set via {@link #setListener}.
223      */
unsubscribe(VmsLayer layer)224     public void unsubscribe(VmsLayer layer) {
225         if (DBG) {
226             Log.d(TAG, "Unsubscribing from layer: " + layer);
227         }
228         VmsSubscriberClientListener listener;
229         synchronized (mListenerLock) {
230             listener = mListener;
231         }
232         if (listener == null) {
233             Log.w(TAG, "unsubscribe: listener was not set, " +
234                     "setListener must be called first.");
235             throw new IllegalStateException("Listener was not set.");
236         }
237         try {
238             mVmsSubscriberService.removeVmsSubscriberClientListener(mIListener, layer);
239         } catch (RemoteException e) {
240             Log.e(TAG, "Failed to unregister subscriber", e);
241             // ignore
242         } catch (IllegalStateException ex) {
243             Car.hideCarNotConnectedExceptionFromCarService(ex);
244         }
245     }
246 
dispatchOnReceiveMessage(VmsLayer layer, byte[] payload)247     private void dispatchOnReceiveMessage(VmsLayer layer, byte[] payload) {
248         VmsSubscriberClientListener listener;
249         synchronized (mListenerLock) {
250             listener = mListener;
251         }
252         if (listener == null) {
253             Log.e(TAG, "Listener died, not dispatching event.");
254             return;
255         }
256         listener.onVmsMessageReceived(layer, payload);
257     }
258 
dispatchOnAvailabilityChangeMessage(List<VmsLayer> availableLayers)259     private void dispatchOnAvailabilityChangeMessage(List<VmsLayer> availableLayers) {
260         VmsSubscriberClientListener listener;
261         synchronized (mListenerLock) {
262             listener = mListener;
263         }
264         if (listener == null) {
265             Log.e(TAG, "Listener died, not dispatching event.");
266             return;
267         }
268         listener.onLayersAvailabilityChange(availableLayers);
269     }
270 
271     /** @hide */
272     @Override
onCarDisconnected()273     public void onCarDisconnected() {
274         clearListener();
275     }
276 
277     private static final class VmsDataMessage {
278         private final VmsLayer mLayer;
279         private final byte[] mPayload;
280 
VmsDataMessage(VmsLayer layer, byte[] payload)281         public VmsDataMessage(VmsLayer layer, byte[] payload) {
282             mLayer = layer;
283             mPayload = payload;
284         }
285 
getLayer()286         public VmsLayer getLayer() {
287             return mLayer;
288         }
getPayload()289         public byte[] getPayload() {
290             return mPayload;
291         }
292     }
293 }
294