1 /* 2 * Copyright (C) 2016 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.hardware.CarPropertyConfig; 21 import android.car.hardware.CarPropertyValue; 22 import android.car.hardware.property.CarPropertyEvent; 23 import android.car.hardware.property.ICarProperty; 24 import android.car.hardware.property.ICarPropertyEventListener; 25 import android.content.Context; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.util.Log; 29 30 import com.android.car.hal.PropertyHalServiceBase; 31 32 import java.io.PrintWriter; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.concurrent.ConcurrentHashMap; 36 37 /** 38 * This class implements the binder interface for ICarProperty.aidl to make it easier to create 39 * multiple managers that deal with Vehicle Properties. To create a new service, simply extend 40 * this class and call the super() constructor with the appropriate arguments for the new service. 41 * {@link CarHvacService} shows the basic usage. 42 */ 43 public class CarPropertyServiceBase extends ICarProperty.Stub 44 implements CarServiceBase, PropertyHalServiceBase.PropertyHalListener { 45 private final Context mContext; 46 private final boolean mDbg; 47 private final Map<IBinder, PropertyDeathRecipient> mDeathRecipientMap = 48 new ConcurrentHashMap<>(); 49 private final PropertyHalServiceBase mHal; 50 private final Map<IBinder, ICarPropertyEventListener> mListenersMap = new ConcurrentHashMap<>(); 51 private final String mPermission; 52 private final String mTag; 53 54 private final Object mLock = new Object(); 55 CarPropertyServiceBase(Context context, PropertyHalServiceBase hal, String permission, boolean dbg, String tag)56 public CarPropertyServiceBase(Context context, PropertyHalServiceBase hal, String permission, 57 boolean dbg, String tag) { 58 mContext = context; 59 mHal = hal; 60 mPermission = permission; 61 mDbg = dbg; 62 mTag = tag + ".service"; 63 } 64 65 class PropertyDeathRecipient implements IBinder.DeathRecipient { 66 private IBinder mListenerBinder; 67 PropertyDeathRecipient(IBinder listenerBinder)68 PropertyDeathRecipient(IBinder listenerBinder) { 69 mListenerBinder = listenerBinder; 70 } 71 72 /** 73 * Client died. Remove the listener from HAL service and unregister if this is the last 74 * client. 75 */ 76 @Override binderDied()77 public void binderDied() { 78 if (mDbg) { 79 Log.d(mTag, "binderDied " + mListenerBinder); 80 } 81 CarPropertyServiceBase.this.unregisterListenerLocked(mListenerBinder); 82 } 83 release()84 void release() { 85 mListenerBinder.unlinkToDeath(this, 0); 86 } 87 } 88 89 @Override init()90 public void init() { 91 } 92 93 @Override release()94 public void release() { 95 for (PropertyDeathRecipient recipient : mDeathRecipientMap.values()) { 96 recipient.release(); 97 } 98 mDeathRecipientMap.clear(); 99 mListenersMap.clear(); 100 } 101 102 @Override dump(PrintWriter writer)103 public void dump(PrintWriter writer) { 104 } 105 106 @Override registerListener(ICarPropertyEventListener listener)107 public void registerListener(ICarPropertyEventListener listener) { 108 if (mDbg) { 109 Log.d(mTag, "registerListener"); 110 } 111 ICarImpl.assertPermission(mContext, mPermission); 112 if (listener == null) { 113 Log.e(mTag, "registerListener: Listener is null."); 114 throw new IllegalArgumentException("listener cannot be null."); 115 } 116 117 IBinder listenerBinder = listener.asBinder(); 118 119 synchronized (mLock) { 120 if (mListenersMap.containsKey(listenerBinder)) { 121 // Already registered, nothing to do. 122 return; 123 } 124 125 PropertyDeathRecipient deathRecipient = new PropertyDeathRecipient(listenerBinder); 126 try { 127 listenerBinder.linkToDeath(deathRecipient, 0); 128 } catch (RemoteException e) { 129 Log.e(mTag, "Failed to link death for recipient. " + e); 130 throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG); 131 } 132 mDeathRecipientMap.put(listenerBinder, deathRecipient); 133 134 if (mListenersMap.isEmpty()) { 135 mHal.setListener(this); 136 } 137 138 mListenersMap.put(listenerBinder, listener); 139 } 140 } 141 142 @Override unregisterListener(ICarPropertyEventListener listener)143 public void unregisterListener(ICarPropertyEventListener listener) { 144 if (mDbg) { 145 Log.d(mTag, "unregisterListener"); 146 } 147 ICarImpl.assertPermission(mContext, mPermission); 148 if (listener == null) { 149 Log.e(mTag, "unregisterListener: Listener is null."); 150 throw new IllegalArgumentException("Listener is null"); 151 } 152 153 IBinder listenerBinder = listener.asBinder(); 154 synchronized (mLock) { 155 if (!mListenersMap.containsKey(listenerBinder)) { 156 Log.e(mTag, "unregisterListener: Listener was not previously registered."); 157 } 158 unregisterListenerLocked(listenerBinder); 159 } 160 } 161 162 // Removes the listenerBinder from the current state. 163 // The function assumes that binder will exist both in listeners and death recipients list. unregisterListenerLocked(IBinder listenerBinder)164 private void unregisterListenerLocked(IBinder listenerBinder) { 165 boolean found = mListenersMap.remove(listenerBinder) != null; 166 167 if (found) { 168 mDeathRecipientMap.get(listenerBinder).release(); 169 mDeathRecipientMap.remove(listenerBinder); 170 } 171 172 if (mListenersMap.isEmpty()) { 173 mHal.setListener(null); 174 } 175 } 176 177 @Override getPropertyList()178 public List<CarPropertyConfig> getPropertyList() { 179 ICarImpl.assertPermission(mContext, mPermission); 180 return mHal.getPropertyList(); 181 } 182 183 @Override getProperty(int prop, int zone)184 public CarPropertyValue getProperty(int prop, int zone) { 185 ICarImpl.assertPermission(mContext, mPermission); 186 return mHal.getProperty(prop, zone); 187 } 188 189 @Override setProperty(CarPropertyValue prop)190 public void setProperty(CarPropertyValue prop) { 191 ICarImpl.assertPermission(mContext, mPermission); 192 mHal.setProperty(prop); 193 } 194 getListeners()195 private ICarPropertyEventListener[] getListeners() { 196 synchronized (mLock) { 197 int size = mListenersMap.values().size(); 198 return mListenersMap.values().toArray(new ICarPropertyEventListener[size]); 199 } 200 } 201 202 // Implement PropertyHalListener interface 203 @Override onPropertyChange(CarPropertyEvent event)204 public void onPropertyChange(CarPropertyEvent event) { 205 for (ICarPropertyEventListener listener : getListeners()) { 206 try { 207 listener.onEvent(event); 208 } catch (RemoteException ex) { 209 // If we could not send a record, its likely the connection snapped. Let the binder 210 // death handle the situation. 211 Log.e(mTag, "onEvent calling failed: " + ex); 212 } 213 } 214 } 215 216 @Override onPropertySetError(int property, int area)217 public void onPropertySetError(int property, int area) { 218 for (ICarPropertyEventListener listener : getListeners()) { 219 try { 220 listener.onEvent(createErrorEvent(property, area)); 221 } catch (RemoteException ex) { 222 // If we could not send a record, its likely the connection snapped. Let the binder 223 // death handle the situation. 224 Log.e(mTag, "onEvent calling failed: " + ex); 225 } 226 } 227 } 228 createErrorEvent(int property, int area)229 private static CarPropertyEvent createErrorEvent(int property, int area) { 230 return new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_ERROR, 231 new CarPropertyValue<>(property, area, null)); 232 } 233 } 234