blob: 84405f4a12f0d2a4b042f4b82ad552a6488eadcc [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 listener that can
* (un)subscribe to different layers. After getting an instance of this manager, the first step
* must be to call #setListener. After that, #subscribe and #unsubscribe methods can be invoked.
* 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 mIListener;
private final Object mListenerLock = new Object();
@GuardedBy("mListenerLock")
private VmsSubscriberClientListener mListener;
/** Interface exposed to VMS subscribers: it is a wrapper of IVmsSubscriberClient. */
public interface VmsSubscriberClientListener {
/** Called when the property is updated */
void onVmsMessageReceived(VmsLayer layer, byte[] payload);
/** Called when layers availability change */
void onLayersAvailabilityChange(List<VmsLayer> availableLayers);
/** Notifies the client of the disconnect event */
void onCarDisconnected();
}
/**
* 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());
mIListener = 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 onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
mHandler.sendMessage(
mHandler.obtainMessage(
VmsEventHandler.ON_AVAILABILITY_CHANGE_EVENT,
availableLayers));
}
};
}
/**
* Sets the listener ({@link #mListener}) this manager is linked to. Subscriptions to the
* {@link com.android.car.VmsSubscriberService} are done through the {@link #mIListener}.
* Therefore, notifications from the {@link com.android.car.VmsSubscriberService} are received
* by the {@link #mIListener} and then forwarded to the {@link #mListener}.
*
* It is expected that this method is invoked just once during the lifetime of the object.
*
* @param listener subscriber listener that will handle onVmsMessageReceived events.
* @throws IllegalStateException if the listener was already set.
*/
public void setListener(VmsSubscriberClientListener listener) {
if (DBG) {
Log.d(TAG, "Setting listener.");
}
synchronized (mListenerLock) {
if (mListener != null) {
throw new IllegalStateException("Listener is already configured.");
}
mListener = listener;
}
}
/**
* Returns a serialized publisher information for a publisher ID.
*/
public byte[] getPublisherInfo(int publisherId) throws CarNotConnectedException, IllegalStateException {
if (DBG) {
Log.d(TAG, "Getting all publishers info.");
}
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 listener was not set via {@link #setListener}.
*/
public void subscribe(VmsLayer layer) throws CarNotConnectedException {
if (DBG) {
Log.d(TAG, "Subscribing to layer: " + layer);
}
VmsSubscriberClientListener listener;
synchronized (mListenerLock) {
listener = mListener;
}
if (listener == null) {
Log.w(TAG, "subscribe: listener was not set, " +
"setListener must be called first.");
throw new IllegalStateException("Listener was not set.");
}
try {
mVmsSubscriberService.addVmsSubscriberClientListener(mIListener, layer);
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
} catch (IllegalStateException ex) {
Car.checkCarNotConnectedExceptionFromCarService(ex);
}
}
public void subscribeAll() throws CarNotConnectedException {
if (DBG) {
Log.d(TAG, "Subscribing passively to all data messages");
}
VmsSubscriberClientListener listener;
synchronized (mListenerLock) {
listener = mListener;
}
if (listener == null) {
Log.w(TAG, "subscribe: listener was not set, " +
"setListener must be called first.");
throw new IllegalStateException("Listener was not set.");
}
try {
mVmsSubscriberService.addVmsSubscriberClientPassiveListener(mIListener);
} 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 listener was not set via {@link #setListener}.
*/
public void unsubscribe(VmsLayer layer) {
if (DBG) {
Log.d(TAG, "Unsubscribing from layer: " + layer);
}
VmsSubscriberClientListener listener;
synchronized (mListenerLock) {
listener = mListener;
}
if (listener == null) {
Log.w(TAG, "unsubscribe: listener was not set, " +
"setListener must be called first.");
throw new IllegalStateException("Listener was not set.");
}
try {
mVmsSubscriberService.removeVmsSubscriberClientListener(mIListener, layer);
} catch (RemoteException e) {
Log.e(TAG, "Failed to unregister subscriber", e);
// ignore
} catch (IllegalStateException ex) {
Car.hideCarNotConnectedExceptionFromCarService(ex);
}
}
public void unsubscribeAll() {
if (DBG) {
Log.d(TAG, "Unsubscribing passively from all data messages");
}
VmsSubscriberClientListener listener;
synchronized (mListenerLock) {
listener = mListener;
}
if (listener == null) {
Log.w(TAG, "unsubscribeAll: listener was not set, " +
"setListener must be called first.");
throw new IllegalStateException("Listener was not set.");
}
try {
mVmsSubscriberService.removeVmsSubscriberClientPassiveListener(mIListener);
} 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) {
VmsSubscriberClientListener listener;
synchronized (mListenerLock) {
listener = mListener;
}
if (listener == null) {
Log.e(TAG, "Listener died, not dispatching event.");
return;
}
listener.onVmsMessageReceived(layer, payload);
}
private void dispatchOnAvailabilityChangeMessage(List<VmsLayer> availableLayers) {
VmsSubscriberClientListener listener;
synchronized (mListenerLock) {
listener = mListener;
}
if (listener == null) {
Log.e(TAG, "Listener died, not dispatching event.");
return;
}
listener.onLayersAvailabilityChange(availableLayers);
}
/** @hide */
@Override
public void onCarDisconnected() {
VmsSubscriberClientListener listener;
synchronized (mListenerLock) {
listener = mListener;
}
if (listener == null) {
Log.e(TAG, "Listener died, not dispatching event.");
return;
}
listener.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;
}
}
}