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; 21 import android.car.vms.IVmsSubscriberClient; 22 import android.car.vms.IVmsSubscriberService; 23 import android.car.vms.VmsLayer; 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; 34 import java.util.Collections; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 40 /** 41 * + Receives HAL updates by implementing VmsHalService.VmsHalListener. 42 * + Offers subscriber/publisher services by implementing IVmsService.Stub. 43 */ 44 @FutureFeature 45 public class VmsSubscriberService extends IVmsSubscriberService.Stub 46 implements CarServiceBase, VmsHalService.VmsHalSubscriberListener { 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; 53 54 @GuardedBy("mSubscriberServiceLock") 55 private final VmsListenerManager mMessageReceivedManager = new VmsListenerManager(); 56 private final Object mSubscriberServiceLock = new Object(); 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 */ 65 private final Object mListenerManagerLock = new Object(); 66 @GuardedBy("mListenerManagerLock") 67 private final Map<IBinder, ListenerDeathRecipient> mListenerDeathRecipientMap = 68 new HashMap<>(); 69 @GuardedBy("mListenerManagerLock") 70 private final Map<IBinder, IVmsSubscriberClient> mListenerMap = new HashMap<>(); 71 72 class ListenerDeathRecipient implements IBinder.DeathRecipient { 73 private IBinder mListenerBinder; 74 ListenerDeathRecipient(IBinder listenerBinder)75 ListenerDeathRecipient(IBinder listenerBinder) { 76 mListenerBinder = listenerBinder; 77 } 78 79 /** 80 * Listener died. Remove it from this service. 81 */ 82 @Override binderDied()83 public void binderDied() { 84 if (DBG) { 85 Log.d(TAG, "binderDied " + mListenerBinder); 86 } 87 88 // Get the Listener from the Binder 89 IVmsSubscriberClient listener = mListenerMap.get(mListenerBinder); 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 101 VmsListenerManager.this.removeListener(mListenerBinder); 102 } 103 release()104 void release() { 105 mListenerBinder.unlinkToDeath(this, 0); 106 } 107 } 108 release()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 */ add(IVmsSubscriberClient listener)125 public void add(IVmsSubscriberClient listener) { 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(); 135 synchronized (mListenerManagerLock) { 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 */ remove(IVmsSubscriberClient listener)158 public void remove(IVmsSubscriberClient listener) { 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. removeListener(IBinder listenerBinder)173 private void removeListener(IBinder listenerBinder) { 174 synchronized (mListenerManagerLock) { 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 */ getListeners()190 public List<IVmsSubscriberClient> getListeners() { 191 synchronized (mListenerManagerLock) { 192 return new ArrayList<>(mListenerMap.values()); 193 } 194 } 195 } 196 VmsSubscriberService(Context context, VmsHalService hal)197 public VmsSubscriberService(Context context, VmsHalService hal) { 198 mContext = context; 199 mHal = hal; 200 } 201 202 // Implements CarServiceBase interface. 203 @Override init()204 public void init() { 205 mHal.addSubscriberListener(this); 206 } 207 208 @Override release()209 public void release() { 210 mMessageReceivedManager.release(); 211 mHal.removeSubscriberListener(this); 212 } 213 214 @Override dump(PrintWriter writer)215 public void dump(PrintWriter writer) { 216 } 217 218 // Implements IVmsService interface. 219 @Override addVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer)220 public void addVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) { 221 synchronized (mSubscriberServiceLock) { 222 // Add the listener so it can subscribe. 223 mMessageReceivedManager.add(listener); 224 225 // Add the subscription for the layer. 226 mHal.addSubscription(listener, layer); 227 } 228 } 229 230 @Override removeVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer)231 public void removeVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) { 232 synchronized (mSubscriberServiceLock) { 233 // Remove the subscription. 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 addVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener)244 public void addVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) { 245 synchronized (mSubscriberServiceLock) { 246 mMessageReceivedManager.add(listener); 247 mHal.addSubscription(listener); 248 } 249 } 250 251 @Override removeVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener)252 public void removeVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) { 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 } 262 } 263 264 @Override getAvailableLayers()265 public List<VmsLayer> getAvailableLayers() { 266 //TODO(asafro): return the list of available layers once logic is implemented. 267 return Collections.emptyList(); 268 } 269 270 // Implements VmsHalSubscriberListener interface 271 @Override onChange(VmsLayer layer, byte[] payload)272 public void onChange(VmsLayer layer, byte[] payload) { 273 if(DBG) { 274 Log.d(TAG, "Publishing a message for layer: " + layer); 275 } 276 277 Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer); 278 279 // If there are no listeners we're done. 280 if ((listeners == null)) { 281 return; 282 } 283 284 for (IVmsSubscriberClient subscriber : listeners) { 285 try { 286 subscriber.onVmsMessageReceived(layer, payload); 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 } 293 294 } 295 } 296