blob: 8f7fba353b215f911f414e53dec83c479944fda8 [file] [log] [blame]
Antonio Cortes6b3544c2017-02-06 16:54:58 -08001/*
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
17package com.android.car;
18
19import android.car.annotation.FutureFeature;
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -070020import android.car.vms.IVmsSubscriberClient;
Antonio Cortes6b3544c2017-02-06 16:54:58 -080021import android.car.vms.IVmsPublisherClient;
22import android.car.vms.IVmsPublisherService;
Antonio Cortes0bd67c32017-03-13 08:02:40 -070023import android.car.vms.VmsLayer;
Asaf Rosenfeld7bd684e2017-03-13 16:24:27 -070024import android.car.vms.VmsLayersOffering;
Antonio Cortes2febe9f2017-03-24 09:42:17 -070025import android.car.vms.VmsSubscriptionState;
Antonio Cortes6b3544c2017-02-06 16:54:58 -080026import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.ServiceConnection;
Asaf Rosenfeld7bd684e2017-03-13 16:24:27 -070030import android.os.Binder;
Antonio Cortes6b3544c2017-02-06 16:54:58 -080031import android.os.IBinder;
32import android.os.RemoteException;
33import android.text.TextUtils;
34import android.util.Log;
Antonio Cortes6b3544c2017-02-06 16:54:58 -080035import com.android.car.hal.VmsHalService;
36import com.android.internal.annotations.GuardedBy;
Antonio Cortes6b3544c2017-02-06 16:54:58 -080037import java.io.PrintWriter;
38import java.lang.ref.WeakReference;
39import java.util.ArrayList;
40import java.util.HashMap;
41import java.util.List;
42import java.util.Map;
asafro25a87402017-02-28 16:08:17 -080043import java.util.Set;
Antonio Cortes6b3544c2017-02-06 16:54:58 -080044
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
51public class VmsPublisherService extends IVmsPublisherService.Stub
asafro99e4f082017-02-09 13:11:42 -080052 implements CarServiceBase, VmsHalService.VmsHalPublisherListener {
Antonio Cortes6b3544c2017-02-06 16:54:58 -080053 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;
Asaf Rosenfeld7bd684e2017-03-13 16:24:27 -070059 private final Map<IBinder, VmsLayersOffering> mRawOffering = new HashMap<>();
Antonio Cortes6b3544c2017-02-06 16:54:58 -080060
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
69 public void init() {
asafro99e4f082017-02-09 13:11:42 -080070 mHal.addPublisherListener(this);
Antonio Cortes6b3544c2017-02-06 16:54:58 -080071 // 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
88 public void release() {
89 mPublisherManager.release();
asafro99e4f082017-02-09 13:11:42 -080090 mHal.removePublisherListener(this);
Antonio Cortes6b3544c2017-02-06 16:54:58 -080091 }
92
93 @Override
94 public void dump(PrintWriter writer) {
95 }
96
Asaf Rosenfeld7bd684e2017-03-13 16:24:27 -070097 @Override
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
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800107 // Implements IVmsPublisherService interface.
108 @Override
Antonio Cortes01a60392017-03-22 13:00:02 -0700109 public void publish(IBinder token, VmsLayer layer, byte[] payload) {
asafro25a87402017-02-28 16:08:17 -0800110 if (DBG) {
Antonio Cortes01a60392017-03-22 13:00:02 -0700111 Log.d(TAG, "Publishing for layer: " + layer);
asafro25a87402017-02-28 16:08:17 -0800112 }
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800113 ICarImpl.assertVmsPublisherPermission(mContext);
asafro25a87402017-02-28 16:08:17 -0800114
Antonio Cortes7e914102017-03-10 09:09:07 -0800115 // Send the message to application listeners.
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700116 Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
asafro25a87402017-02-28 16:08:17 -0800117
118 if (DBG) {
119 Log.d(TAG, "Number of subscribed apps: " + listeners.size());
120 }
Asaf Rosenfeld2ca6f8c2017-03-20 12:59:33 -0700121 for (IVmsSubscriberClient listener : listeners) {
asafro25a87402017-02-28 16:08:17 -0800122 try {
Antonio Cortes01a60392017-03-22 13:00:02 -0700123 listener.onVmsMessageReceived(layer, payload);
asafro25a87402017-02-28 16:08:17 -0800124 } 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");
Antonio Cortes01a60392017-03-22 13:00:02 -0700132 mHal.setDataMessage(layer, payload);
asafro25a87402017-02-28 16:08:17 -0800133 } else {
134 Log.d(TAG, "HAL is NOT subscribed");
135 }
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800136 }
137
138 @Override
Antonio Cortes2febe9f2017-03-24 09:42:17 -0700139 public VmsSubscriptionState getSubscriptions() {
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800140 ICarImpl.assertVmsPublisherPermission(mContext);
Antonio Cortes2febe9f2017-03-24 09:42:17 -0700141 return mHal.getSubscriptionState();
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800142 }
143
144 // Implements VmsHalListener interface
Antonio Cortes9db963a2017-03-10 15:15:28 -0800145 /**
146 * This method is only invoked by VmsHalService.notifyPublishers which is synchronized.
147 * Therefore this method only sees a non-decreasing sequence.
148 */
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800149 @Override
Antonio Cortes2febe9f2017-03-24 09:42:17 -0700150 public void onChange(VmsSubscriptionState subscriptionState) {
Antonio Cortes7e914102017-03-10 09:09:07 -0800151 // Send the message to application listeners.
152 for (IVmsPublisherClient client : mPublisherManager.getClients()) {
153 try {
Antonio Cortes2febe9f2017-03-24 09:42:17 -0700154 client.onVmsSubscriptionChange(subscriptionState);
Antonio Cortes7e914102017-03-10 09:09:07 -0800155 } catch (RemoteException ex) {
156 Log.e(TAG, "unable to send notification to: " + client, ex);
157 }
asafro25a87402017-02-28 16:08:17 -0800158 }
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800159 }
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
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 */
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);
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800198 PublisherConnection connection = new PublisherConnection();
199 if (publisherService.mContext.bindService(intent, connection,
Antonio Cortesaf103c12017-02-16 07:37:35 -0800200 Context.BIND_AUTO_CREATE)) {
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800201 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 */
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 */
237 public List<IVmsPublisherClient> getClients() {
238 synchronized (mLock) {
239 return new ArrayList<>(mPublisherMap.values());
240 }
241 }
242
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 {
Asaf Rosenfeld7bd684e2017-03-13 16:24:27 -0700254
255 private final IBinder mToken = new Binder();
256
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800257 /**
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
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 {
Asaf Rosenfeld7bd684e2017-03-13 16:24:27 -0700274 service.setVmsPublisherService(mToken, publisherService);
Antonio Cortes6b3544c2017-02-06 16:54:58 -0800275 } 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
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}