blob: 33ca1f3037a78f9e05000905a715012c863c04de [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.app.Service;
import android.car.annotation.FutureFeature;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.annotation.Nullable;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.lang.ref.WeakReference;
/**
* Services that need VMS publisher services need to inherit from this class and also need to be
* declared in the array vmsPublisherClients located in
* packages/services/Car/service/res/values/config.xml (most likely, this file will be in an overlay
* of the target product.
*
* The {@link com.android.car.VmsPublisherService} will start this service. The callback
* {@link #onVmsPublisherServiceReady()} notifies when VMS publisher services can be used, and the
* publisher can request a publisher ID in order to start publishing.
*
* SystemApi candidate.
*
* @hide
*/
@FutureFeature
public abstract class VmsPublisherClientService extends Service {
private static final boolean DBG = true;
private static final String TAG = "VmsPublisherClient";
private final Object mLock = new Object();
private Handler mHandler = new VmsEventHandler(this);
private final VmsPublisherClientBinder mVmsPublisherClient = new VmsPublisherClientBinder(this);
private volatile IVmsPublisherService mVmsPublisherService = null;
@GuardedBy("mLock")
private IBinder mToken = null;
@Override
public final IBinder onBind(Intent intent) {
if (DBG) {
Log.d(TAG, "onBind, intent: " + intent);
}
return mVmsPublisherClient.asBinder();
}
@Override
public final boolean onUnbind(Intent intent) {
if (DBG) {
Log.d(TAG, "onUnbind, intent: " + intent);
}
stopSelf();
return super.onUnbind(intent);
}
private void setToken(IBinder token) {
synchronized (mLock) {
mToken = token;
}
}
/**
* Notifies that the publisher services are ready.
*/
protected abstract void onVmsPublisherServiceReady();
/**
* Publishers need to implement this method to receive notifications of subscription changes.
*
* @param subscriptionState the state of the subscriptions.
*/
public abstract void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState);
/**
* Uses the VmsPublisherService binder to publish messages.
*
* @param layer the layer to publish to.
* @param payload the message to be sent.
* @param publisherId the ID that got assigned to the publisher that published the message by
* VMS core.
* @return if the call to the method VmsPublisherService.publish was successful.
*/
public final void publish(VmsLayer layer, int publisherId, byte[] payload) {
if (DBG) {
Log.d(TAG, "Publishing for layer : " + layer);
}
IBinder token = getTokenForPublisherServiceThreadSafe();
try {
mVmsPublisherService.publish(token, layer, publisherId, payload);
} catch (RemoteException e) {
Log.e(TAG, "unable to publish message: " + payload, e);
}
}
/**
* Uses the VmsPublisherService binder to set the layers offering.
*
* @param offering the layers that the publisher may publish.
* @return if the call to VmsPublisherService.setLayersOffering was successful.
*/
public final void setLayersOffering(VmsLayersOffering offering) {
if (DBG) {
Log.d(TAG, "Setting layers offering : " + offering);
}
IBinder token = getTokenForPublisherServiceThreadSafe();
try {
mVmsPublisherService.setLayersOffering(token, offering);
VmsOperationRecorder.get().setLayersOffering(offering);
} catch (RemoteException e) {
Log.e(TAG, "unable to set layers offering: " + offering, e);
}
}
private IBinder getTokenForPublisherServiceThreadSafe() {
if (mVmsPublisherService == null) {
throw new IllegalStateException("VmsPublisherService not set.");
}
IBinder token;
synchronized (mLock) {
token = mToken;
}
if (token == null) {
throw new IllegalStateException("VmsPublisherService does not have a valid token.");
}
return token;
}
public final int getPublisherId(byte[] publisherInfo) {
if (mVmsPublisherService == null) {
throw new IllegalStateException("VmsPublisherService not set.");
}
Integer publisherId = null;
try {
Log.i(TAG, "Getting publisher static ID");
publisherId = mVmsPublisherService.getPublisherId(publisherInfo);
} catch (RemoteException e) {
Log.e(TAG, "unable to invoke binder method.", e);
}
if (publisherId == null) {
throw new IllegalStateException("VmsPublisherService cannot get a publisher static ID.");
} else {
VmsOperationRecorder.get().getPublisherId(publisherId);
}
return publisherId;
}
/**
* Uses the VmsPublisherService binder to get the state of the subscriptions.
*
* @return list of layer/version or null in case of error.
*/
public final @Nullable VmsSubscriptionState getSubscriptions() {
if (mVmsPublisherService == null) {
throw new IllegalStateException("VmsPublisherService not set.");
}
try {
return mVmsPublisherService.getSubscriptions();
} catch (RemoteException e) {
Log.e(TAG, "unable to invoke binder method.", e);
}
return null;
}
private void setVmsPublisherService(IVmsPublisherService service) {
mVmsPublisherService = service;
onVmsPublisherServiceReady();
}
/**
* Implements the interface that the VMS service uses to communicate with this client.
*/
private static class VmsPublisherClientBinder extends IVmsPublisherClient.Stub {
private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService;
@GuardedBy("mSequenceLock")
private long mSequence = -1;
private final Object mSequenceLock = new Object();
public VmsPublisherClientBinder(VmsPublisherClientService vmsPublisherClientService) {
mVmsPublisherClientService = new WeakReference<>(vmsPublisherClientService);
}
@Override
public void setVmsPublisherService(IBinder token, IVmsPublisherService service)
throws RemoteException {
VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get();
if (vmsPublisherClientService == null) return;
if (DBG) {
Log.d(TAG, "setting VmsPublisherService.");
}
Handler handler = vmsPublisherClientService.mHandler;
handler.sendMessage(
handler.obtainMessage(VmsEventHandler.SET_SERVICE_CALLBACK, service));
vmsPublisherClientService.setToken(token);
}
@Override
public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState)
throws RemoteException {
VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get();
if (vmsPublisherClientService == null) return;
if (DBG) {
Log.d(TAG, "subscription event: " + subscriptionState);
}
synchronized (mSequenceLock) {
if (subscriptionState.getSequenceNumber() <= mSequence) {
Log.w(TAG, "Sequence out of order. Current sequence = " + mSequence
+ "; expected new sequence = " + subscriptionState.getSequenceNumber());
// Do not propagate old notifications.
return;
} else {
mSequence = subscriptionState.getSequenceNumber();
}
}
Handler handler = vmsPublisherClientService.mHandler;
handler.sendMessage(
handler.obtainMessage(VmsEventHandler.ON_SUBSCRIPTION_CHANGE_EVENT,
subscriptionState));
}
}
/**
* Receives events from the binder thread and dispatches them.
*/
private final static class VmsEventHandler extends Handler {
/** Constants handled in the handler */
private static final int ON_SUBSCRIPTION_CHANGE_EVENT = 0;
private static final int SET_SERVICE_CALLBACK = 1;
private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService;
VmsEventHandler(VmsPublisherClientService service) {
super(Looper.getMainLooper());
mVmsPublisherClientService = new WeakReference<>(service);
}
@Override
public void handleMessage(Message msg) {
VmsPublisherClientService service = mVmsPublisherClientService.get();
if (service == null) return;
switch (msg.what) {
case ON_SUBSCRIPTION_CHANGE_EVENT:
VmsSubscriptionState subscriptionState = (VmsSubscriptionState) msg.obj;
service.onVmsSubscriptionChange(subscriptionState);
break;
case SET_SERVICE_CALLBACK:
service.setVmsPublisherService((IVmsPublisherService) msg.obj);
break;
default:
Log.e(TAG, "Event type not handled: " + msg.what);
break;
}
}
}
}