Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 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.Car; |
| 20 | import android.car.annotation.FutureFeature; |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 21 | import android.car.vms.IVmsSubscriberClient; |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 22 | import android.car.vms.IVmsSubscriberService; |
Antonio Cortes | 0bd67c3 | 2017-03-13 08:02:40 -0700 | [diff] [blame] | 23 | import android.car.vms.VmsLayer; |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 24 | import android.content.Context; |
| 25 | import android.os.IBinder; |
| 26 | import android.os.RemoteException; |
| 27 | import android.util.Log; |
| 28 | |
| 29 | import com.android.car.hal.VmsHalService; |
| 30 | import com.android.internal.annotations.GuardedBy; |
| 31 | |
| 32 | import java.io.PrintWriter; |
| 33 | import java.util.ArrayList; |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 34 | import java.util.Collections; |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 35 | import java.util.HashMap; |
| 36 | import java.util.List; |
| 37 | import java.util.Map; |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 38 | import java.util.Set; |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 39 | |
| 40 | /** |
| 41 | * + Receives HAL updates by implementing VmsHalService.VmsHalListener. |
| 42 | * + Offers subscriber/publisher services by implementing IVmsService.Stub. |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 43 | */ |
| 44 | @FutureFeature |
| 45 | public class VmsSubscriberService extends IVmsSubscriberService.Stub |
asafro | 99e4f08 | 2017-02-09 13:11:42 -0800 | [diff] [blame] | 46 | implements CarServiceBase, VmsHalService.VmsHalSubscriberListener { |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 47 | private static final boolean DBG = true; |
| 48 | private static final String PERMISSION = Car.PERMISSION_VMS_SUBSCRIBER; |
| 49 | private static final String TAG = "VmsSubscriberService"; |
| 50 | |
| 51 | private final Context mContext; |
| 52 | private final VmsHalService mHal; |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 53 | |
| 54 | @GuardedBy("mSubscriberServiceLock") |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 55 | private final VmsListenerManager mMessageReceivedManager = new VmsListenerManager(); |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 56 | private final Object mSubscriberServiceLock = new Object(); |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 57 | |
| 58 | /** |
| 59 | * Keeps track of listeners of this service. |
| 60 | */ |
| 61 | class VmsListenerManager { |
| 62 | /** |
| 63 | * Allows to modify mListenerMap and mListenerDeathRecipientMap as a single unit. |
| 64 | */ |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 65 | private final Object mListenerManagerLock = new Object(); |
| 66 | @GuardedBy("mListenerManagerLock") |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 67 | private final Map<IBinder, ListenerDeathRecipient> mListenerDeathRecipientMap = |
| 68 | new HashMap<>(); |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 69 | @GuardedBy("mListenerManagerLock") |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 70 | private final Map<IBinder, IVmsSubscriberClient> mListenerMap = new HashMap<>(); |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 71 | |
| 72 | class ListenerDeathRecipient implements IBinder.DeathRecipient { |
| 73 | private IBinder mListenerBinder; |
| 74 | |
| 75 | ListenerDeathRecipient(IBinder listenerBinder) { |
| 76 | mListenerBinder = listenerBinder; |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Listener died. Remove it from this service. |
| 81 | */ |
| 82 | @Override |
| 83 | public void binderDied() { |
| 84 | if (DBG) { |
| 85 | Log.d(TAG, "binderDied " + mListenerBinder); |
| 86 | } |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 87 | |
| 88 | // Get the Listener from the Binder |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 89 | IVmsSubscriberClient listener = mListenerMap.get(mListenerBinder); |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 90 | |
| 91 | // Remove the listener subscriptions. |
| 92 | if (listener != null) { |
| 93 | Log.d(TAG, "Removing subscriptions for dead listener: " + listener); |
| 94 | mHal.removeDeadListener(listener); |
| 95 | } else { |
| 96 | Log.d(TAG, "Handling dead binder with no matching listener"); |
| 97 | |
| 98 | } |
| 99 | |
| 100 | // Remove binder |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 101 | VmsListenerManager.this.removeListener(mListenerBinder); |
| 102 | } |
| 103 | |
| 104 | void release() { |
| 105 | mListenerBinder.unlinkToDeath(this, 0); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | public void release() { |
| 110 | for (ListenerDeathRecipient recipient : mListenerDeathRecipientMap.values()) { |
| 111 | recipient.release(); |
| 112 | } |
| 113 | mListenerDeathRecipientMap.clear(); |
| 114 | mListenerMap.clear(); |
| 115 | } |
| 116 | |
| 117 | /** |
| 118 | * Adds the listener and a death recipient associated to it. |
| 119 | * |
| 120 | * @param listener to be added. |
| 121 | * @throws IllegalArgumentException if the listener is null. |
| 122 | * @throws IllegalStateException if it was not possible to link a death recipient to the |
| 123 | * listener. |
| 124 | */ |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 125 | public void add(IVmsSubscriberClient listener) { |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 126 | ICarImpl.assertPermission(mContext, PERMISSION); |
| 127 | if (listener == null) { |
| 128 | Log.e(TAG, "register: listener is null."); |
| 129 | throw new IllegalArgumentException("listener cannot be null."); |
| 130 | } |
| 131 | if (DBG) { |
| 132 | Log.d(TAG, "register: " + listener); |
| 133 | } |
| 134 | IBinder listenerBinder = listener.asBinder(); |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 135 | synchronized (mListenerManagerLock) { |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 136 | if (mListenerMap.containsKey(listenerBinder)) { |
| 137 | // Already registered, nothing to do. |
| 138 | return; |
| 139 | } |
| 140 | ListenerDeathRecipient deathRecipient = new ListenerDeathRecipient(listenerBinder); |
| 141 | try { |
| 142 | listenerBinder.linkToDeath(deathRecipient, 0); |
| 143 | } catch (RemoteException e) { |
| 144 | Log.e(TAG, "Failed to link death for recipient. ", e); |
| 145 | throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG); |
| 146 | } |
| 147 | mListenerDeathRecipientMap.put(listenerBinder, deathRecipient); |
| 148 | mListenerMap.put(listenerBinder, listener); |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Removes the listener and associated death recipient. |
| 154 | * |
| 155 | * @param listener to be removed. |
| 156 | * @throws IllegalArgumentException if listener is null. |
| 157 | */ |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 158 | public void remove(IVmsSubscriberClient listener) { |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 159 | if (DBG) { |
| 160 | Log.d(TAG, "unregisterListener"); |
| 161 | } |
| 162 | ICarImpl.assertPermission(mContext, PERMISSION); |
| 163 | if (listener == null) { |
| 164 | Log.e(TAG, "unregister: listener is null."); |
| 165 | throw new IllegalArgumentException("Listener is null"); |
| 166 | } |
| 167 | IBinder listenerBinder = listener.asBinder(); |
| 168 | removeListener(listenerBinder); |
| 169 | } |
| 170 | |
| 171 | // Removes the listenerBinder from the current state. |
| 172 | // The function assumes that binder will exist both in listeners and death recipients list. |
| 173 | private void removeListener(IBinder listenerBinder) { |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 174 | synchronized (mListenerManagerLock) { |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 175 | boolean found = mListenerMap.remove(listenerBinder) != null; |
| 176 | if (found) { |
| 177 | mListenerDeathRecipientMap.get(listenerBinder).release(); |
| 178 | mListenerDeathRecipientMap.remove(listenerBinder); |
| 179 | } else { |
| 180 | Log.e(TAG, "removeListener: listener was not previously registered."); |
| 181 | } |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * Returns list of listeners currently registered. |
| 187 | * |
| 188 | * @return list of listeners. |
| 189 | */ |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 190 | public List<IVmsSubscriberClient> getListeners() { |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 191 | synchronized (mListenerManagerLock) { |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 192 | return new ArrayList<>(mListenerMap.values()); |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | public VmsSubscriberService(Context context, VmsHalService hal) { |
| 198 | mContext = context; |
| 199 | mHal = hal; |
| 200 | } |
| 201 | |
| 202 | // Implements CarServiceBase interface. |
| 203 | @Override |
| 204 | public void init() { |
asafro | 99e4f08 | 2017-02-09 13:11:42 -0800 | [diff] [blame] | 205 | mHal.addSubscriberListener(this); |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | @Override |
| 209 | public void release() { |
| 210 | mMessageReceivedManager.release(); |
asafro | 99e4f08 | 2017-02-09 13:11:42 -0800 | [diff] [blame] | 211 | mHal.removeSubscriberListener(this); |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | @Override |
| 215 | public void dump(PrintWriter writer) { |
| 216 | } |
| 217 | |
| 218 | // Implements IVmsService interface. |
| 219 | @Override |
Antonio Cortes | 01a6039 | 2017-03-22 13:00:02 -0700 | [diff] [blame] | 220 | public void addVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) { |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 221 | synchronized (mSubscriberServiceLock) { |
| 222 | // Add the listener so it can subscribe. |
| 223 | mMessageReceivedManager.add(listener); |
| 224 | |
| 225 | // Add the subscription for the layer. |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 226 | mHal.addSubscription(listener, layer); |
| 227 | } |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | @Override |
Antonio Cortes | 01a6039 | 2017-03-22 13:00:02 -0700 | [diff] [blame] | 231 | public void removeVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) { |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 232 | synchronized (mSubscriberServiceLock) { |
| 233 | // Remove the subscription. |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 234 | mHal.removeSubscription(listener, layer); |
| 235 | |
| 236 | // Remove the listener if it has no more subscriptions. |
| 237 | if (!mHal.containsListener(listener)) { |
| 238 | mMessageReceivedManager.remove(listener); |
| 239 | } |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | @Override |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 244 | public void addVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) { |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 245 | synchronized (mSubscriberServiceLock) { |
| 246 | mMessageReceivedManager.add(listener); |
| 247 | mHal.addSubscription(listener); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | @Override |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 252 | public void removeVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) { |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 253 | synchronized (mSubscriberServiceLock) { |
| 254 | // Remove the subscription. |
| 255 | mHal.removeSubscription(listener); |
| 256 | |
| 257 | // Remove the listener if it has no more subscriptions. |
| 258 | if (!mHal.containsListener(listener)) { |
| 259 | mMessageReceivedManager.remove(listener); |
| 260 | } |
| 261 | } |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 262 | } |
| 263 | |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 264 | @Override |
| 265 | public List<VmsLayer> getAvailableLayers() { |
| 266 | //TODO(asafro): return the list of available layers once logic is implemented. |
| 267 | return Collections.emptyList(); |
| 268 | } |
| 269 | |
asafro | 99e4f08 | 2017-02-09 13:11:42 -0800 | [diff] [blame] | 270 | // Implements VmsHalSubscriberListener interface |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 271 | @Override |
Antonio Cortes | 01a6039 | 2017-03-22 13:00:02 -0700 | [diff] [blame] | 272 | public void onChange(VmsLayer layer, byte[] payload) { |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 273 | if(DBG) { |
| 274 | Log.d(TAG, "Publishing a message for layer: " + layer); |
| 275 | } |
| 276 | |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 277 | Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer); |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 278 | |
| 279 | // If there are no listeners we're done. |
| 280 | if ((listeners == null)) { |
| 281 | return; |
| 282 | } |
| 283 | |
Asaf Rosenfeld | 2ca6f8c | 2017-03-20 12:59:33 -0700 | [diff] [blame] | 284 | for (IVmsSubscriberClient subscriber : listeners) { |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 285 | try { |
Antonio Cortes | 01a6039 | 2017-03-22 13:00:02 -0700 | [diff] [blame] | 286 | subscriber.onVmsMessageReceived(layer, payload); |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 287 | } catch (RemoteException e) { |
| 288 | // If we could not send a record, its likely the connection snapped. Let the binder |
| 289 | // death handle the situation. |
| 290 | Log.e(TAG, "onVmsMessageReceived calling failed: ", e); |
| 291 | } |
| 292 | } |
asafro | 25a8740 | 2017-02-28 16:08:17 -0800 | [diff] [blame] | 293 | |
Antonio Cortes | e4619c7 | 2017-02-02 07:53:27 -0800 | [diff] [blame] | 294 | } |
| 295 | } |