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.NonNull; 21 import android.annotation.SystemApi; 22 import android.app.Service; 23 import android.car.Car; 24 import android.content.Intent; 25 import android.os.Binder; 26 import android.os.Build; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.Process; 32 import android.os.RemoteException; 33 import android.util.Log; 34 35 import com.android.internal.annotations.GuardedBy; 36 import com.android.internal.util.Preconditions; 37 38 import java.lang.ref.WeakReference; 39 import java.util.Collections; 40 41 /** 42 * API implementation of a Vehicle Map Service publisher client. 43 * 44 * All publisher clients must inherit from this class and export it as a service, and the service 45 * be added to either the {@code vmsPublisherSystemClients} or {@code vmsPublisherUserClients} 46 * arrays in the Car service configuration, depending on which user the client will run as. 47 * 48 * The {@link com.android.car.VmsPublisherService} will then bind to this service, with the 49 * {@link #onVmsPublisherServiceReady()} callback notifying the client implementation when the 50 * connection is established and publisher operations can be called. 51 * 52 * Publishers must also register a publisher ID by calling {@link #getPublisherId(byte[])}. 53 * 54 * @hide 55 */ 56 @SystemApi 57 public abstract class VmsPublisherClientService extends Service { 58 private static final boolean DBG = false; 59 private static final String TAG = "VmsPublisherClientService"; 60 61 private static final VmsSubscriptionState DEFAULT_SUBSCRIPTIONS = 62 new VmsSubscriptionState(0, Collections.emptySet(), 63 Collections.emptySet()); 64 65 private final Object mLock = new Object(); 66 67 private Handler mHandler = new VmsEventHandler(this); 68 private final VmsPublisherClientBinder mVmsPublisherClient = new VmsPublisherClientBinder(this); 69 private volatile IVmsPublisherService mVmsPublisherService = null; 70 @GuardedBy("mLock") 71 private IBinder mToken = null; 72 73 @Override onBind(Intent intent)74 public IBinder onBind(Intent intent) { 75 if (DBG) Log.d(TAG, "onBind, intent: " + intent); 76 return mVmsPublisherClient.asBinder(); 77 } 78 79 @Override onUnbind(Intent intent)80 public boolean onUnbind(Intent intent) { 81 if (DBG) Log.d(TAG, "onUnbind, intent: " + intent); 82 stopSelf(); 83 return super.onUnbind(intent); 84 } 85 setToken(IBinder token)86 private void setToken(IBinder token) { 87 synchronized (mLock) { 88 mToken = token; 89 } 90 } 91 92 /** 93 * Notifies the client that publisher services are ready. 94 */ onVmsPublisherServiceReady()95 protected abstract void onVmsPublisherServiceReady(); 96 97 /** 98 * Notifies the client of changes in layer subscriptions. 99 * 100 * @param subscriptionState state of layer subscriptions 101 */ onVmsSubscriptionChange(@onNull VmsSubscriptionState subscriptionState)102 public abstract void onVmsSubscriptionChange(@NonNull VmsSubscriptionState subscriptionState); 103 104 /** 105 * Publishes a data packet to subscribers. 106 * 107 * Publishers must only publish packets for the layers that they have made offerings for. 108 * 109 * @param layer layer to publish to 110 * @param publisherId ID of the publisher publishing the message 111 * @param payload data packet to be sent 112 * @throws IllegalStateException if publisher services are not available 113 */ publish(@onNull VmsLayer layer, int publisherId, byte[] payload)114 public final void publish(@NonNull VmsLayer layer, int publisherId, byte[] payload) { 115 Preconditions.checkNotNull(layer, "layer cannot be null"); 116 if (DBG) Log.d(TAG, "Publishing for layer : " + layer); 117 118 IBinder token = getTokenForPublisherServiceThreadSafe(); 119 120 try { 121 mVmsPublisherService.publish(token, layer, publisherId, payload); 122 } catch (RemoteException e) { 123 Car.handleRemoteExceptionFromCarService(this, e); 124 } 125 } 126 127 /** 128 * Sets the layers offered by a specific publisher. 129 * 130 * @param offering layers being offered for subscription by the publisher 131 * @throws IllegalStateException if publisher services are not available 132 */ setLayersOffering(@onNull VmsLayersOffering offering)133 public final void setLayersOffering(@NonNull VmsLayersOffering offering) { 134 Preconditions.checkNotNull(offering, "offering cannot be null"); 135 if (DBG) Log.d(TAG, "Setting layers offering : " + offering); 136 137 IBinder token = getTokenForPublisherServiceThreadSafe(); 138 139 try { 140 mVmsPublisherService.setLayersOffering(token, offering); 141 VmsOperationRecorder.get().setLayersOffering(offering); 142 } catch (RemoteException e) { 143 Car.handleRemoteExceptionFromCarService(this, e); 144 } 145 } 146 getTokenForPublisherServiceThreadSafe()147 private IBinder getTokenForPublisherServiceThreadSafe() { 148 if (mVmsPublisherService == null) { 149 throw new IllegalStateException("VmsPublisherService not set."); 150 } 151 152 IBinder token; 153 synchronized (mLock) { 154 token = mToken; 155 } 156 if (token == null) { 157 throw new IllegalStateException("VmsPublisherService does not have a valid token."); 158 } 159 return token; 160 } 161 162 /** 163 * Acquires a publisher ID for a serialized publisher description. 164 * 165 * Multiple calls to this method with the same information will return the same publisher ID. 166 * 167 * @param publisherInfo serialized publisher description information, in a vendor-specific 168 * format 169 * @return a publisher ID for the given publisher description 170 * @throws IllegalStateException if publisher services are not available 171 */ getPublisherId(byte[] publisherInfo)172 public final int getPublisherId(byte[] publisherInfo) { 173 if (mVmsPublisherService == null) { 174 throw new IllegalStateException("VmsPublisherService not set."); 175 } 176 int publisherId; 177 try { 178 publisherId = mVmsPublisherService.getPublisherId(publisherInfo); 179 Log.i(TAG, "Assigned publisher ID: " + publisherId); 180 } catch (RemoteException e) { 181 // This will crash. To prevent crash, safer invalid return value should be defined. 182 throw e.rethrowFromSystemServer(); 183 } 184 VmsOperationRecorder.get().getPublisherId(publisherId); 185 return publisherId; 186 } 187 188 /** 189 * Gets the state of layer subscriptions. 190 * 191 * @return state of layer subscriptions 192 * @throws IllegalStateException if publisher services are not available 193 */ getSubscriptions()194 public final VmsSubscriptionState getSubscriptions() { 195 if (mVmsPublisherService == null) { 196 throw new IllegalStateException("VmsPublisherService not set."); 197 } 198 try { 199 return mVmsPublisherService.getSubscriptions(); 200 } catch (RemoteException e) { 201 return Car.handleRemoteExceptionFromCarService(this, e, DEFAULT_SUBSCRIPTIONS); 202 } 203 } 204 setVmsPublisherService(IVmsPublisherService service)205 private void setVmsPublisherService(IVmsPublisherService service) { 206 mVmsPublisherService = service; 207 onVmsPublisherServiceReady(); 208 } 209 210 /** 211 * Implements the interface that the VMS service uses to communicate with this client. 212 */ 213 private static class VmsPublisherClientBinder extends IVmsPublisherClient.Stub { 214 private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService; 215 @GuardedBy("mSequenceLock") 216 private long mSequence = -1; 217 private final Object mSequenceLock = new Object(); 218 VmsPublisherClientBinder(VmsPublisherClientService vmsPublisherClientService)219 VmsPublisherClientBinder(VmsPublisherClientService vmsPublisherClientService) { 220 mVmsPublisherClientService = new WeakReference<>(vmsPublisherClientService); 221 } 222 223 @Override setVmsPublisherService(IBinder token, IVmsPublisherService service)224 public void setVmsPublisherService(IBinder token, IVmsPublisherService service) { 225 assertSystemOrSelf(); 226 227 VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get(); 228 if (vmsPublisherClientService == null) return; 229 if (DBG) Log.d(TAG, "setting VmsPublisherService."); 230 Handler handler = vmsPublisherClientService.mHandler; 231 handler.sendMessage( 232 handler.obtainMessage(VmsEventHandler.SET_SERVICE_CALLBACK, service)); 233 vmsPublisherClientService.setToken(token); 234 } 235 236 @Override onVmsSubscriptionChange(VmsSubscriptionState subscriptionState)237 public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) { 238 assertSystemOrSelf(); 239 240 VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get(); 241 if (vmsPublisherClientService == null) return; 242 if (DBG) Log.d(TAG, "subscription event: " + subscriptionState); 243 synchronized (mSequenceLock) { 244 if (subscriptionState.getSequenceNumber() <= mSequence) { 245 Log.w(TAG, "Sequence out of order. Current sequence = " + mSequence 246 + "; expected new sequence = " + subscriptionState.getSequenceNumber()); 247 // Do not propagate old notifications. 248 return; 249 } else { 250 mSequence = subscriptionState.getSequenceNumber(); 251 } 252 } 253 Handler handler = vmsPublisherClientService.mHandler; 254 handler.sendMessage( 255 handler.obtainMessage(VmsEventHandler.ON_SUBSCRIPTION_CHANGE_EVENT, 256 subscriptionState)); 257 } 258 assertSystemOrSelf()259 private void assertSystemOrSelf() { 260 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 261 if (DBG) Log.d(TAG, "Skipping system user check"); 262 return; 263 } 264 265 if (!(Binder.getCallingUid() == Process.SYSTEM_UID 266 || Binder.getCallingPid() == Process.myPid())) { 267 throw new SecurityException("Caller must be system user or same process"); 268 } 269 } 270 } 271 272 /** 273 * Receives events from the binder thread and dispatches them. 274 */ 275 private final static class VmsEventHandler extends Handler { 276 /** Constants handled in the handler */ 277 private static final int ON_SUBSCRIPTION_CHANGE_EVENT = 0; 278 private static final int SET_SERVICE_CALLBACK = 1; 279 280 private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService; 281 VmsEventHandler(VmsPublisherClientService service)282 VmsEventHandler(VmsPublisherClientService service) { 283 super(Looper.getMainLooper()); 284 mVmsPublisherClientService = new WeakReference<>(service); 285 } 286 287 @Override handleMessage(Message msg)288 public void handleMessage(Message msg) { 289 VmsPublisherClientService service = mVmsPublisherClientService.get(); 290 if (service == null) return; 291 switch (msg.what) { 292 case ON_SUBSCRIPTION_CHANGE_EVENT: 293 VmsSubscriptionState subscriptionState = (VmsSubscriptionState) msg.obj; 294 service.onVmsSubscriptionChange(subscriptionState); 295 break; 296 case SET_SERVICE_CALLBACK: 297 service.setVmsPublisherService((IVmsPublisherService) msg.obj); 298 break; 299 default: 300 Log.e(TAG, "Event type not handled: " + msg.what); 301 break; 302 } 303 } 304 } 305 } 306