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 20 import android.annotation.SystemApi; 21 import android.app.Service; 22 import android.content.Intent; 23 import android.os.Handler; 24 import android.os.IBinder; 25 import android.os.Looper; 26 import android.os.Message; 27 import android.os.RemoteException; 28 import android.annotation.Nullable; 29 import android.util.Log; 30 31 import com.android.internal.annotations.GuardedBy; 32 33 import java.lang.ref.WeakReference; 34 35 /** 36 * Services that need VMS publisher services need to inherit from this class and also need to be 37 * declared in the array vmsPublisherClients located in 38 * packages/services/Car/service/res/values/config.xml (most likely, this file will be in an overlay 39 * of the target product. 40 * 41 * The {@link com.android.car.VmsPublisherService} will start this service. The callback 42 * {@link #onVmsPublisherServiceReady()} notifies when VMS publisher services can be used, and the 43 * publisher can request a publisher ID in order to start publishing. 44 * 45 * SystemApi candidate. 46 * 47 * @hide 48 */ 49 @SystemApi 50 public abstract class VmsPublisherClientService extends Service { 51 private static final boolean DBG = true; 52 private static final String TAG = "VmsPublisherClient"; 53 54 private final Object mLock = new Object(); 55 56 private Handler mHandler = new VmsEventHandler(this); 57 private final VmsPublisherClientBinder mVmsPublisherClient = new VmsPublisherClientBinder(this); 58 private volatile IVmsPublisherService mVmsPublisherService = null; 59 @GuardedBy("mLock") 60 private IBinder mToken = null; 61 62 @Override 63 public IBinder onBind(Intent intent) { 64 if (DBG) { 65 Log.d(TAG, "onBind, intent: " + intent); 66 } 67 return mVmsPublisherClient.asBinder(); 68 } 69 70 @Override 71 public boolean onUnbind(Intent intent) { 72 if (DBG) { 73 Log.d(TAG, "onUnbind, intent: " + intent); 74 } 75 stopSelf(); 76 return super.onUnbind(intent); 77 } 78 79 private void setToken(IBinder token) { 80 synchronized (mLock) { 81 mToken = token; 82 } 83 } 84 85 /** 86 * Notifies that the publisher services are ready. 87 */ 88 protected abstract void onVmsPublisherServiceReady(); 89 90 /** 91 * Publishers need to implement this method to receive notifications of subscription changes. 92 * 93 * @param subscriptionState the state of the subscriptions. 94 */ 95 public abstract void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState); 96 97 /** 98 * Uses the VmsPublisherService binder to publish messages. 99 * 100 * @param layer the layer to publish to. 101 * @param payload the message to be sent. 102 * @param publisherId the ID that got assigned to the publisher that published the message by 103 * VMS core. 104 * @return if the call to the method VmsPublisherService.publish was successful. 105 */ 106 public final void publish(VmsLayer layer, int publisherId, byte[] payload) { 107 if (DBG) { 108 Log.d(TAG, "Publishing for layer : " + layer); 109 } 110 111 IBinder token = getTokenForPublisherServiceThreadSafe(); 112 113 try { 114 mVmsPublisherService.publish(token, layer, publisherId, payload); 115 } catch (RemoteException e) { 116 Log.e(TAG, "unable to publish message: " + payload, e); 117 } 118 } 119 120 /** 121 * Uses the VmsPublisherService binder to set the layers offering. 122 * 123 * @param offering the layers that the publisher may publish. 124 * @return if the call to VmsPublisherService.setLayersOffering was successful. 125 */ 126 public final void setLayersOffering(VmsLayersOffering offering) { 127 if (DBG) { 128 Log.d(TAG, "Setting layers offering : " + offering); 129 } 130 131 IBinder token = getTokenForPublisherServiceThreadSafe(); 132 133 try { 134 mVmsPublisherService.setLayersOffering(token, offering); 135 VmsOperationRecorder.get().setLayersOffering(offering); 136 } catch (RemoteException e) { 137 Log.e(TAG, "unable to set layers offering: " + offering, e); 138 } 139 } 140 141 private IBinder getTokenForPublisherServiceThreadSafe() { 142 if (mVmsPublisherService == null) { 143 throw new IllegalStateException("VmsPublisherService not set."); 144 } 145 146 IBinder token; 147 synchronized (mLock) { 148 token = mToken; 149 } 150 if (token == null) { 151 throw new IllegalStateException("VmsPublisherService does not have a valid token."); 152 } 153 return token; 154 } 155 156 public final int getPublisherId(byte[] publisherInfo) { 157 if (mVmsPublisherService == null) { 158 throw new IllegalStateException("VmsPublisherService not set."); 159 } 160 Integer publisherId = null; 161 try { 162 Log.i(TAG, "Getting publisher static ID"); 163 publisherId = mVmsPublisherService.getPublisherId(publisherInfo); 164 } catch (RemoteException e) { 165 Log.e(TAG, "unable to invoke binder method.", e); 166 } 167 if (publisherId == null) { 168 throw new IllegalStateException("VmsPublisherService cannot get a publisher static ID."); 169 } else { 170 VmsOperationRecorder.get().getPublisherId(publisherId); 171 } 172 return publisherId; 173 } 174 175 /** 176 * Uses the VmsPublisherService binder to get the state of the subscriptions. 177 * 178 * @return list of layer/version or null in case of error. 179 */ 180 public final @Nullable VmsSubscriptionState getSubscriptions() { 181 if (mVmsPublisherService == null) { 182 throw new IllegalStateException("VmsPublisherService not set."); 183 } 184 try { 185 return mVmsPublisherService.getSubscriptions(); 186 } catch (RemoteException e) { 187 Log.e(TAG, "unable to invoke binder method.", e); 188 } 189 return null; 190 } 191 192 private void setVmsPublisherService(IVmsPublisherService service) { 193 mVmsPublisherService = service; 194 onVmsPublisherServiceReady(); 195 } 196 197 /** 198 * Implements the interface that the VMS service uses to communicate with this client. 199 */ 200 private static class VmsPublisherClientBinder extends IVmsPublisherClient.Stub { 201 private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService; 202 @GuardedBy("mSequenceLock") 203 private long mSequence = -1; 204 private final Object mSequenceLock = new Object(); 205 206 public VmsPublisherClientBinder(VmsPublisherClientService vmsPublisherClientService) { 207 mVmsPublisherClientService = new WeakReference<>(vmsPublisherClientService); 208 } 209 210 @Override 211 public void setVmsPublisherService(IBinder token, IVmsPublisherService service) 212 throws RemoteException { 213 VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get(); 214 if (vmsPublisherClientService == null) return; 215 if (DBG) { 216 Log.d(TAG, "setting VmsPublisherService."); 217 } 218 Handler handler = vmsPublisherClientService.mHandler; 219 handler.sendMessage( 220 handler.obtainMessage(VmsEventHandler.SET_SERVICE_CALLBACK, service)); 221 vmsPublisherClientService.setToken(token); 222 } 223 224 @Override 225 public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) 226 throws RemoteException { 227 VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get(); 228 if (vmsPublisherClientService == null) return; 229 if (DBG) { 230 Log.d(TAG, "subscription event: " + subscriptionState); 231 } 232 synchronized (mSequenceLock) { 233 if (subscriptionState.getSequenceNumber() <= mSequence) { 234 Log.w(TAG, "Sequence out of order. Current sequence = " + mSequence 235 + "; expected new sequence = " + subscriptionState.getSequenceNumber()); 236 // Do not propagate old notifications. 237 return; 238 } else { 239 mSequence = subscriptionState.getSequenceNumber(); 240 } 241 } 242 Handler handler = vmsPublisherClientService.mHandler; 243 handler.sendMessage( 244 handler.obtainMessage(VmsEventHandler.ON_SUBSCRIPTION_CHANGE_EVENT, 245 subscriptionState)); 246 } 247 } 248 249 /** 250 * Receives events from the binder thread and dispatches them. 251 */ 252 private final static class VmsEventHandler extends Handler { 253 /** Constants handled in the handler */ 254 private static final int ON_SUBSCRIPTION_CHANGE_EVENT = 0; 255 private static final int SET_SERVICE_CALLBACK = 1; 256 257 private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService; 258 259 VmsEventHandler(VmsPublisherClientService service) { 260 super(Looper.getMainLooper()); 261 mVmsPublisherClientService = new WeakReference<>(service); 262 } 263 264 @Override 265 public void handleMessage(Message msg) { 266 VmsPublisherClientService service = mVmsPublisherClientService.get(); 267 if (service == null) return; 268 switch (msg.what) { 269 case ON_SUBSCRIPTION_CHANGE_EVENT: 270 VmsSubscriptionState subscriptionState = (VmsSubscriptionState) msg.obj; 271 service.onVmsSubscriptionChange(subscriptionState); 272 break; 273 case SET_SERVICE_CALLBACK: 274 service.setVmsPublisherService((IVmsPublisherService) msg.obj); 275 break; 276 default: 277 Log.e(TAG, "Event type not handled: " + msg.what); 278 break; 279 } 280 } 281 } 282 } 283