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