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