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
17package android.car.vms;
18
19import android.car.Car;
20import android.car.CarManagerBase;
21import android.car.CarNotConnectedException;
22import android.car.annotation.FutureFeature;
23import android.os.Handler;
24import android.os.IBinder;
25import android.os.Looper;
26import android.os.Message;
27import android.os.RemoteException;
28import android.util.Log;
29import com.android.internal.annotations.GuardedBy;
30import java.lang.ref.WeakReference;
31import 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
42public 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 */
56        void onVmsMessageReceived(VmsLayer layer, byte[] payload);
57
58        /** Called when layers availability change */
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
72        VmsEventHandler(VmsSubscriberManager mgr, Looper looper) {
73            super(looper);
74            mMgr = new WeakReference<>(mgr);
75        }
76
77        @Override
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
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     */
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     */
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     */
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
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     */
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
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
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
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
281        public VmsDataMessage(VmsLayer layer, byte[] payload) {
282            mLayer = layer;
283            mPayload = payload;
284        }
285
286        public VmsLayer getLayer() {
287            return mLayer;
288        }
289        public byte[] getPayload() {
290            return mPayload;
291        }
292    }
293}
294