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