blob: 6f6377ca7f1227e7764ab4966f8ae5322e4ce7d6 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.car.vms;
import android.car.Car;
import android.car.CarManagerBase;
import android.car.CarNotConnectedException;
import android.car.annotation.FutureFeature;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.lang.ref.WeakReference;
import java.util.List;
/**
* API for interfacing with the VmsSubscriberService. It supports a single client callback that can
* (un)subscribe to different layers. Getting notifactions and managing subscriptions is enabled
* after setting the client callback with #registerClientCallback.
* SystemApi candidate
*
* @hide
*/
@FutureFeature
public final class VmsSubscriberManager implements CarManagerBase {
private static final boolean DBG = true;
private static final String TAG = "VmsSubscriberManager";
private final Handler mHandler;
private final IVmsSubscriberService mVmsSubscriberService;
private final IVmsSubscriberClient mSubscriberManagerClient;
private final Object mClientCallbackLock = new Object();
@GuardedBy("mClientCallbackLock")
private VmsSubscriberClientCallback mClientCallback;
/**
* Interface exposed to VMS subscribers: it is a wrapper of IVmsSubscriberClient.
*/
public interface VmsSubscriberClientCallback {
/**
* Called when the property is updated
*/
void onVmsMessageReceived(VmsLayer layer, byte[] payload);
/**
* Called when layers availability change
*/
void onLayersAvailabilityChanged(List<VmsLayer> availableLayers);
}
/**
* Allows to asynchronously dispatch onVmsMessageReceived events.
*/
private final static class VmsEventHandler extends Handler {
/**
* Constants handled in the handler
*/
private static final int ON_RECEIVE_MESSAGE_EVENT = 0;
private static final int ON_AVAILABILITY_CHANGE_EVENT = 1;
private final WeakReference<VmsSubscriberManager> mMgr;
VmsEventHandler(VmsSubscriberManager mgr, Looper looper) {
super(looper);
mMgr = new WeakReference<>(mgr);
}
@Override
public void handleMessage(Message msg) {
VmsSubscriberManager mgr = mMgr.get();
switch (msg.what) {
case ON_RECEIVE_MESSAGE_EVENT:
if (mgr != null) {
// Parse the message
VmsDataMessage vmsDataMessage = (VmsDataMessage) msg.obj;
// Dispatch the parsed message
mgr.dispatchOnReceiveMessage(vmsDataMessage.getLayer(),
vmsDataMessage.getPayload());
}
break;
case ON_AVAILABILITY_CHANGE_EVENT:
if (mgr != null) {
// Parse the message
List<VmsLayer> vmsAvailabilityChangeMessage = (List<VmsLayer>) msg.obj;
// Dispatch the parsed message
mgr.dispatchOnAvailabilityChangeMessage(vmsAvailabilityChangeMessage);
}
break;
default:
Log.e(VmsSubscriberManager.TAG, "Event type not handled: " + msg.what);
break;
}
}
}
public VmsSubscriberManager(IBinder service, Handler handler) {
mVmsSubscriberService = IVmsSubscriberService.Stub.asInterface(service);
mHandler = new VmsEventHandler(this, handler.getLooper());
mSubscriberManagerClient = new IVmsSubscriberClient.Stub() {
@Override
public void onVmsMessageReceived(VmsLayer layer, byte[] payload)
throws RemoteException {
// Create the data message
VmsDataMessage vmsDataMessage = new VmsDataMessage(layer, payload);
mHandler.sendMessage(
mHandler.obtainMessage(
VmsEventHandler.ON_RECEIVE_MESSAGE_EVENT,
vmsDataMessage));
}
@Override
public void onLayersAvailabilityChanged(List<VmsAssociatedLayer> availableLayers) {
mHandler.sendMessage(
mHandler.obtainMessage(
VmsEventHandler.ON_AVAILABILITY_CHANGE_EVENT,
availableLayers));
}
};
}
/**
* Registers the client callback in order to enable communication with the client.
* By registering, the client will start getting notifications, and will be able to subscribe
* to layers.
* <p>
*
* @param clientCallback subscriber callback that will handle onVmsMessageReceived events.
* @throws IllegalStateException if the client callback was already set.
*/
public void registerClientCallback(VmsSubscriberClientCallback clientCallback)
throws CarNotConnectedException {
synchronized (mClientCallbackLock) {
if (mClientCallback != null) {
throw new IllegalStateException("Client callback is already configured.");
}
mClientCallback = clientCallback;
}
try {
mVmsSubscriberService.addVmsSubscriberToNotifications(mSubscriberManagerClient);
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
}
}
/**
* Unregisters the client callback which disables communication with the client.
* @throws CarNotConnectedException, IllegalStateException
*/
public void unregisterClientCallback()
throws CarNotConnectedException {
try {
mVmsSubscriberService.removeVmsSubscriberToNotifications(mSubscriberManagerClient);
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
} catch (IllegalStateException e) {
Log.e(TAG, "Could not unsubscribe from notifications");
throw e;
}
synchronized (mClientCallbackLock) {
mClientCallback = null;
}
}
/**
* Returns a serialized publisher information for a publisher ID.
*/
public byte[] getPublisherInfo(int publisherId)
throws CarNotConnectedException, IllegalStateException {
try {
return mVmsSubscriberService.getPublisherInfo(publisherId);
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
} catch (IllegalStateException ex) {
Car.checkCarNotConnectedExceptionFromCarService(ex);
throw new IllegalStateException(ex);
}
}
/**
* Subscribes to listen to the layer specified.
*
* @param layer the layer to subscribe to.
* @throws IllegalStateException if the client callback was not set via
* {@link #registerClientCallback}.
*/
public void subscribe(VmsLayer layer) throws CarNotConnectedException {
verifySubscriptionIsAllowed();
try {
mVmsSubscriberService.addVmsSubscriber(mSubscriberManagerClient, layer);
VmsOperationRecorder.get().subscribe(layer);
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
} catch (IllegalStateException ex) {
Car.checkCarNotConnectedExceptionFromCarService(ex);
}
}
/**
* Subscribes to listen to the layer specified from the publisher specified.
*
* @param layer the layer to subscribe to.
* @param publisherId the publisher of the layer.
* @throws IllegalStateException if the client callback was not set via
* {@link #registerClientCallback}.
*/
public void subscribe(VmsLayer layer, int publisherId) throws CarNotConnectedException {
verifySubscriptionIsAllowed();
try {
mVmsSubscriberService.addVmsSubscriberToPublisher(
mSubscriberManagerClient, layer, publisherId);
VmsOperationRecorder.get().subscribe(layer, publisherId);
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
} catch (IllegalStateException ex) {
Car.checkCarNotConnectedExceptionFromCarService(ex);
}
}
public void startMonitoring() throws CarNotConnectedException {
verifySubscriptionIsAllowed();
try {
mVmsSubscriberService.addVmsSubscriberPassive(mSubscriberManagerClient);
VmsOperationRecorder.get().startMonitoring();
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
} catch (IllegalStateException ex) {
Car.checkCarNotConnectedExceptionFromCarService(ex);
}
}
/**
* Unsubscribes from the layer/version specified.
*
* @param layer the layer to unsubscribe from.
* @throws IllegalStateException if the client callback was not set via
* {@link #registerClientCallback}.
*/
public void unsubscribe(VmsLayer layer) {
verifySubscriptionIsAllowed();
try {
mVmsSubscriberService.removeVmsSubscriber(mSubscriberManagerClient, layer);
VmsOperationRecorder.get().unsubscribe(layer);
} catch (RemoteException e) {
Log.e(TAG, "Failed to unregister subscriber", e);
// ignore
} catch (IllegalStateException ex) {
Car.hideCarNotConnectedExceptionFromCarService(ex);
}
}
/**
* Unsubscribes from the layer/version specified.
*
* @param layer the layer to unsubscribe from.
* @param publisherId the pubisher of the layer.
* @throws IllegalStateException if the client callback was not set via
* {@link #registerClientCallback}.
*/
public void unsubscribe(VmsLayer layer, int publisherId) {
try {
mVmsSubscriberService.removeVmsSubscriberToPublisher(
mSubscriberManagerClient, layer, publisherId);
VmsOperationRecorder.get().unsubscribe(layer, publisherId);
} catch (RemoteException e) {
Log.e(TAG, "Failed to unregister subscriber", e);
// ignore
} catch (IllegalStateException ex) {
Car.hideCarNotConnectedExceptionFromCarService(ex);
}
}
public void stopMonitoring() {
try {
mVmsSubscriberService.removeVmsSubscriberPassive(mSubscriberManagerClient);
VmsOperationRecorder.get().stopMonitoring();
} catch (RemoteException e) {
Log.e(TAG, "Failed to unregister subscriber ", e);
// ignore
} catch (IllegalStateException ex) {
Car.hideCarNotConnectedExceptionFromCarService(ex);
}
}
private void dispatchOnReceiveMessage(VmsLayer layer, byte[] payload) {
VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
if (clientCallback == null) {
Log.e(TAG, "Cannot dispatch received message.");
return;
}
clientCallback.onVmsMessageReceived(layer, payload);
}
private void dispatchOnAvailabilityChangeMessage(List<VmsLayer> availableLayers) {
VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
if (clientCallback == null) {
Log.e(TAG, "Cannot dispatch availability change message.");
return;
}
clientCallback.onLayersAvailabilityChanged(availableLayers);
}
private VmsSubscriberClientCallback getClientCallbackThreadSafe() {
VmsSubscriberClientCallback clientCallback;
synchronized (mClientCallbackLock) {
clientCallback = mClientCallback;
}
if (clientCallback == null) {
Log.e(TAG, "client callback not set.");
}
return clientCallback;
}
/*
* Verifies that the subscriber is in a state where it is allowed to subscribe.
*/
private void verifySubscriptionIsAllowed() {
VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
if (clientCallback == null) {
throw new IllegalStateException("Cannot subscribe.");
}
}
/**
* @hide
*/
@Override
public void onCarDisconnected() {
}
private static final class VmsDataMessage {
private final VmsLayer mLayer;
private final byte[] mPayload;
public VmsDataMessage(VmsLayer layer, byte[] payload) {
mLayer = layer;
mPayload = payload;
}
public VmsLayer getLayer() {
return mLayer;
}
public byte[] getPayload() {
return mPayload;
}
}
}