• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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