• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.car;
18 
19 import android.car.annotation.FutureFeature;
20 import android.car.vms.IVmsSubscriberClient;
21 import android.car.vms.IVmsPublisherClient;
22 import android.car.vms.IVmsPublisherService;
23 import android.car.vms.VmsLayer;
24 import android.car.vms.VmsLayersOffering;
25 import android.car.vms.VmsSubscriptionState;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.ServiceConnection;
30 import android.os.Binder;
31 import android.os.IBinder;
32 import android.os.RemoteException;
33 import android.text.TextUtils;
34 import android.util.Log;
35 import com.android.car.hal.VmsHalService;
36 import com.android.internal.annotations.GuardedBy;
37 import java.io.PrintWriter;
38 import java.lang.ref.WeakReference;
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Set;
44 
45 /**
46  * + Receives HAL updates by implementing VmsHalService.VmsHalListener.
47  * + Binds to publishers and configures them to use this service.
48  * + Notifies publishers of subscription changes.
49  */
50 @FutureFeature
51 public class VmsPublisherService extends IVmsPublisherService.Stub
52         implements CarServiceBase, VmsHalService.VmsHalPublisherListener {
53     private static final boolean DBG = true;
54     private static final String TAG = "VmsPublisherService";
55 
56     private final Context mContext;
57     private final VmsHalService mHal;
58     private final VmsPublisherManager mPublisherManager;
59     private final Map<IBinder, VmsLayersOffering> mRawOffering = new HashMap<>();
60 
VmsPublisherService(Context context, VmsHalService hal)61     public VmsPublisherService(Context context, VmsHalService hal) {
62         mContext = context;
63         mHal = hal;
64         mPublisherManager = new VmsPublisherManager(this);
65     }
66 
67     // Implements CarServiceBase interface.
68     @Override
init()69     public void init() {
70         mHal.addPublisherListener(this);
71         // Launch publishers.
72         String[] publisherNames = mContext.getResources().getStringArray(
73                 R.array.vmsPublisherClients);
74         for (String publisherName : publisherNames) {
75             if (TextUtils.isEmpty(publisherName)) {
76                 Log.e(TAG, "empty publisher name");
77                 continue;
78             }
79             ComponentName name = ComponentName.unflattenFromString(publisherName);
80             if (name == null) {
81                 Log.e(TAG, "invalid publisher name: " + publisherName);
82             }
83             mPublisherManager.bind(name);
84         }
85     }
86 
87     @Override
release()88     public void release() {
89         mPublisherManager.release();
90         mHal.removePublisherListener(this);
91     }
92 
93     @Override
dump(PrintWriter writer)94     public void dump(PrintWriter writer) {
95     }
96 
97     @Override
setLayersOffering(IBinder token, VmsLayersOffering offering)98     public void setLayersOffering(IBinder token, VmsLayersOffering offering) {
99         // Store the raw dependencies
100         mRawOffering.put(token, offering);
101 
102         //TODO(asafro): Calculate the new available layers
103 
104         //TODO(asafro): Notify the subscribers that there is a change in availability
105     }
106 
107     // Implements IVmsPublisherService interface.
108     @Override
publish(IBinder token, VmsLayer layer, byte[] payload)109     public void publish(IBinder token, VmsLayer layer, byte[] payload) {
110         if (DBG) {
111             Log.d(TAG, "Publishing for layer: " + layer);
112         }
113         ICarImpl.assertVmsPublisherPermission(mContext);
114 
115         // Send the message to application listeners.
116         Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
117 
118         if (DBG) {
119             Log.d(TAG, "Number of subscribed apps: " + listeners.size());
120         }
121         for (IVmsSubscriberClient listener : listeners) {
122             try {
123                 listener.onVmsMessageReceived(layer, payload);
124             } catch (RemoteException ex) {
125                 Log.e(TAG, "unable to publish to listener: " + listener);
126             }
127         }
128 
129         // Send the message to HAL
130         if (mHal.isHalSubscribed(layer)) {
131             Log.d(TAG, "HAL is subscribed");
132             mHal.setDataMessage(layer, payload);
133         } else {
134             Log.d(TAG, "HAL is NOT subscribed");
135         }
136     }
137 
138     @Override
getSubscriptions()139     public VmsSubscriptionState getSubscriptions() {
140         ICarImpl.assertVmsPublisherPermission(mContext);
141         return mHal.getSubscriptionState();
142     }
143 
144     // Implements VmsHalListener interface
145     /**
146      * This method is only invoked by VmsHalService.notifyPublishers which is synchronized.
147      * Therefore this method only sees a non-decreasing sequence.
148      */
149     @Override
onChange(VmsSubscriptionState subscriptionState)150     public void onChange(VmsSubscriptionState subscriptionState) {
151         // Send the message to application listeners.
152         for (IVmsPublisherClient client : mPublisherManager.getClients()) {
153             try {
154                 client.onVmsSubscriptionChange(subscriptionState);
155             } catch (RemoteException ex) {
156                 Log.e(TAG, "unable to send notification to: " + client, ex);
157             }
158         }
159     }
160 
161     /**
162      * Keeps track of publishers that are using this service.
163      */
164     private static class VmsPublisherManager {
165         /**
166          * Allows to modify mPublisherMap and mPublisherConnectionMap as a single unit.
167          */
168         private final Object mLock = new Object();
169         @GuardedBy("mLock")
170         private final Map<String, PublisherConnection> mPublisherConnectionMap = new HashMap<>();
171         @GuardedBy("mLock")
172         private final Map<String, IVmsPublisherClient> mPublisherMap = new HashMap<>();
173         private final WeakReference<VmsPublisherService> mPublisherService;
174 
VmsPublisherManager(VmsPublisherService publisherService)175         public VmsPublisherManager(VmsPublisherService publisherService) {
176             mPublisherService = new WeakReference<>(publisherService);
177         }
178 
179         /**
180          * Tries to bind to a publisher.
181          *
182          * @param name publisher component name (e.g. android.car.vms.logger/.LoggingService).
183          */
bind(ComponentName name)184         public void bind(ComponentName name) {
185             VmsPublisherService publisherService = mPublisherService.get();
186             if (publisherService == null) return;
187             String publisherName = name.flattenToString();
188             if (DBG) {
189                 Log.d(TAG, "binding to: " + publisherName);
190             }
191             synchronized (mLock) {
192                 if (mPublisherConnectionMap.containsKey(publisherName)) {
193                     // Already registered, nothing to do.
194                     return;
195                 }
196                 Intent intent = new Intent();
197                 intent.setComponent(name);
198                 PublisherConnection connection = new PublisherConnection();
199                 if (publisherService.mContext.bindService(intent, connection,
200                         Context.BIND_AUTO_CREATE)) {
201                     mPublisherConnectionMap.put(publisherName, connection);
202                 } else {
203                     Log.e(TAG, "unable to bind to: " + publisherName);
204                 }
205             }
206         }
207 
208         /**
209          * Removes the publisher and associated connection.
210          *
211          * @param name publisher component name (e.g. android.car.vms.Logger).
212          */
unbind(ComponentName name)213         public void unbind(ComponentName name) {
214             VmsPublisherService publisherService = mPublisherService.get();
215             if (publisherService == null) return;
216             String publisherName = name.flattenToString();
217             if (DBG) {
218                 Log.d(TAG, "unbinding from: " + publisherName);
219             }
220             synchronized (mLock) {
221                 boolean found = mPublisherMap.remove(publisherName) != null;
222                 if (found) {
223                     PublisherConnection connection = mPublisherConnectionMap.get(publisherName);
224                     publisherService.mContext.unbindService(connection);
225                     mPublisherConnectionMap.remove(publisherName);
226                 } else {
227                     Log.e(TAG, "unbind: unknown publisher." + publisherName);
228                 }
229             }
230         }
231 
232         /**
233          * Returns the list of publishers currently registered.
234          *
235          * @return list of publishers.
236          */
getClients()237         public List<IVmsPublisherClient> getClients() {
238             synchronized (mLock) {
239                 return new ArrayList<>(mPublisherMap.values());
240             }
241         }
242 
release()243         public void release() {
244             VmsPublisherService publisherService = mPublisherService.get();
245             if (publisherService == null) return;
246             for (PublisherConnection connection : mPublisherConnectionMap.values()) {
247                 publisherService.mContext.unbindService(connection);
248             }
249             mPublisherConnectionMap.clear();
250             mPublisherMap.clear();
251         }
252 
253         class PublisherConnection implements ServiceConnection {
254 
255             private final IBinder mToken = new Binder();
256 
257             /**
258              * Once the service binds to a publisher service, the publisher binder is added to
259              * mPublisherMap
260              * and the publisher is configured to use this service.
261              */
262             @Override
onServiceConnected(ComponentName name, IBinder binder)263             public void onServiceConnected(ComponentName name, IBinder binder) {
264                 VmsPublisherService publisherService = mPublisherService.get();
265                 if (publisherService == null) return;
266                 if (DBG) {
267                     Log.d(TAG, "onServiceConnected, name: " + name + ", binder: " + binder);
268                 }
269                 IVmsPublisherClient service = IVmsPublisherClient.Stub.asInterface(binder);
270                 synchronized (mLock) {
271                     mPublisherMap.put(name.flattenToString(), service);
272                 }
273                 try {
274                     service.setVmsPublisherService(mToken, publisherService);
275                 } catch (RemoteException e) {
276                     Log.e(TAG, "unable to configure publisher: " + name);
277                 }
278             }
279 
280             /**
281              * Tries to rebind to the publisher service.
282              */
283             @Override
onServiceDisconnected(ComponentName name)284             public void onServiceDisconnected(ComponentName name) {
285                 String publisherName = name.flattenToString();
286                 Log.d(TAG, "onServiceDisconnected, name: " + publisherName);
287                 VmsPublisherManager.this.unbind(name);
288                 VmsPublisherManager.this.bind(name);
289             }
290         }
291     }
292 }
293