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