• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 android.car.hardware.property;
18 
19 import static java.lang.Integer.toHexString;
20 
21 import android.car.CarApiUtil;
22 import android.car.CarManagerBase;
23 import android.car.CarNotConnectedException;
24 import android.car.hardware.CarPropertyConfig;
25 import android.car.hardware.CarPropertyValue;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.ArraySet;
30 import android.util.Log;
31 import android.util.SparseArray;
32 
33 import com.android.car.internal.CarRatedFloatListeners;
34 import com.android.car.internal.SingleMessageHandler;
35 
36 import java.lang.ref.WeakReference;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.function.Consumer;
40 
41 
42 /**
43  * API for creating Car*Manager
44  * @hide
45  */
46 public class CarPropertyManager implements CarManagerBase {
47     private final boolean mDbg;
48     private final SingleMessageHandler<CarPropertyEvent> mHandler;
49     private final ICarProperty mService;
50     private final String mTag;
51     private static final int MSG_GENERIC_EVENT = 0;
52 
53     private CarPropertyEventListenerToService mCarPropertyEventToService;
54 
55 
56     /** Record of locally active properties. Key is propertyId */
57     private final SparseArray<CarPropertyListeners> mActivePropertyListener =
58             new SparseArray<>();
59 
60     /** Callback functions for property events */
61     public interface CarPropertyEventListener {
62         /** Called when a property is updated */
onChangeEvent(CarPropertyValue value)63         void onChangeEvent(CarPropertyValue value);
64 
65         /** Called when an error is detected with a property */
onErrorEvent(int propId, int zone)66         void onErrorEvent(int propId, int zone);
67     }
68 
69     /**
70      * Get an instance of the CarPropertyManager.
71      */
CarPropertyManager(IBinder service, Handler handler, boolean dbg, String tag)72     public CarPropertyManager(IBinder service, Handler handler, boolean dbg, String tag) {
73         mDbg = dbg;
74         mTag = tag;
75         mService = ICarProperty.Stub.asInterface(service);
76         mHandler = new SingleMessageHandler<CarPropertyEvent>(handler.getLooper(),
77                 MSG_GENERIC_EVENT) {
78             @Override
79             protected void handleEvent(CarPropertyEvent event) {
80                 CarPropertyListeners listeners;
81                 synchronized (mActivePropertyListener) {
82                     listeners = mActivePropertyListener.get(
83                             event.getCarPropertyValue().getPropertyId());
84                 }
85                 if (listeners != null) {
86                     switch (event.getEventType()) {
87                         case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE:
88                             listeners.onPropertyChanged(event);
89                             break;
90                         case CarPropertyEvent.PROPERTY_EVENT_ERROR:
91                             listeners.onErrorEvent(event);
92                             break;
93                         default:
94                             throw new IllegalArgumentException();
95                     }
96                 }
97             }
98         };
99     }
100 
101     /** Use to register or update Callback for properties */
registerListener(CarPropertyEventListener listener, int propertyId, float rate)102     public boolean registerListener(CarPropertyEventListener listener, int propertyId, float rate)
103             throws CarNotConnectedException {
104         synchronized (mActivePropertyListener) {
105             if (mCarPropertyEventToService == null) {
106                 mCarPropertyEventToService = new CarPropertyEventListenerToService(this);
107             }
108             boolean needsServerUpdate = false;
109             CarPropertyListeners listeners;
110             listeners = mActivePropertyListener.get(propertyId);
111             if (listeners == null) {
112                 listeners = new CarPropertyListeners(rate);
113                 mActivePropertyListener.put(propertyId, listeners);
114                 needsServerUpdate = true;
115             }
116             if (listeners.addAndUpdateRate(listener, rate)) {
117                 needsServerUpdate = true;
118             }
119             if (needsServerUpdate) {
120                 if (!registerOrUpdatePropertyListener(propertyId, rate)) {
121                     return false;
122                 }
123             }
124         }
125         return true;
126     }
127 
registerOrUpdatePropertyListener(int propertyId, float rate)128     private boolean registerOrUpdatePropertyListener(int propertyId, float rate)
129             throws CarNotConnectedException {
130         try {
131             mService.registerListener(propertyId, rate, mCarPropertyEventToService);
132         } catch (IllegalStateException e) {
133             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
134         } catch (RemoteException e) {
135             throw new CarNotConnectedException(e);
136         }
137         return true;
138     }
139 
140     private class CarPropertyEventListenerToService extends ICarPropertyEventListener.Stub{
141         private final WeakReference<CarPropertyManager> mMgr;
142 
CarPropertyEventListenerToService(CarPropertyManager mgr)143         CarPropertyEventListenerToService(CarPropertyManager mgr) {
144             mMgr = new WeakReference<>(mgr);
145         }
146 
147         @Override
onEvent(List<CarPropertyEvent> events)148         public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
149             CarPropertyManager manager = mMgr.get();
150             if (manager != null) {
151                 manager.handleEvent(events);
152             }
153         }
154     }
155 
handleEvent(List<CarPropertyEvent> events)156     private void handleEvent(List<CarPropertyEvent> events) {
157         mHandler.sendEvents(events);
158     }
159 
160     /**
161      * Stop getting sensor update for the given listener. If there are multiple registrations for
162      * this listener, all listening will be stopped.
163      * @param listener
164      */
unregisterListener(CarPropertyEventListener listener)165     public void unregisterListener(CarPropertyEventListener listener) {
166         synchronized (mActivePropertyListener) {
167             for (int i = 0; i < mActivePropertyListener.size(); i++) {
168                 doUnregisterListenerLocked(listener, mActivePropertyListener.keyAt(i));
169             }
170         }
171     }
172 
173     /**
174      * Stop getting sensor update for the given listener and sensor. If the same listener is used
175      * for other sensors, those subscriptions will not be affected.
176      * @param listener
177      * @param propertyId
178      */
unregisterListener(CarPropertyEventListener listener, int propertyId)179     public void unregisterListener(CarPropertyEventListener listener, int propertyId) {
180         synchronized (mActivePropertyListener) {
181             doUnregisterListenerLocked(listener, propertyId);
182         }
183     }
184 
doUnregisterListenerLocked(CarPropertyEventListener listener, int propertyId)185     private void doUnregisterListenerLocked(CarPropertyEventListener listener, int propertyId) {
186         CarPropertyListeners listeners = mActivePropertyListener.get(propertyId);
187         if (listeners != null) {
188             boolean needsServerUpdate = false;
189             if (listeners.contains(listener)) {
190                 needsServerUpdate = listeners.remove(listener);
191             }
192             if (listeners.isEmpty()) {
193                 try {
194                     mService.unregisterListener(propertyId, mCarPropertyEventToService);
195                 } catch (RemoteException e) {
196                     //ignore
197                 }
198                 mActivePropertyListener.remove(propertyId);
199             } else if (needsServerUpdate) {
200                 try {
201                     registerOrUpdatePropertyListener(propertyId, listeners.getRate());
202                 } catch (CarNotConnectedException e) {
203                     // ignore
204                 }
205             }
206         }
207     }
208 
209     /**
210      * Returns the list of properties implemented by this car.
211      *
212      * @return Caller must check the property type and typecast to the appropriate subclass
213      * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty)
214      */
getPropertyList()215     public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
216         try {
217             return mService.getPropertyList();
218         } catch (RemoteException e) {
219             Log.e(mTag, "getPropertyList exception ", e);
220             throw new CarNotConnectedException(e);
221         }
222     }
223 
224     /**
225      * Returns the list of properties implemented by this car in given property id list.
226      *
227      * @return Caller must check the property type and typecast to the appropriate subclass
228      * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty)
229      */
getPropertyList(ArraySet<Integer> propertyIds)230     public List<CarPropertyConfig> getPropertyList(ArraySet<Integer> propertyIds)
231             throws CarNotConnectedException {
232         try {
233             List<CarPropertyConfig> configs = new ArrayList<>();
234             for (CarPropertyConfig c : mService.getPropertyList()) {
235                 if (propertyIds.contains(c.getPropertyId())) {
236                     configs.add(c);
237                 }
238             }
239             return configs;
240         } catch (RemoteException e) {
241             Log.e(mTag, "getPropertyList exception ", e);
242             throw new CarNotConnectedException(e);
243         }
244 
245     }
246 
247     /**
248      * Check whether a given property is available or disabled based on the car's current state.
249      * @return true if STATUS_AVAILABLE, false otherwise (eg STATUS_UNAVAILABLE)
250      * @throws CarNotConnectedException
251      */
isPropertyAvailable(int propId, int area)252     public boolean isPropertyAvailable(int propId, int area) throws CarNotConnectedException {
253         try {
254             CarPropertyValue propValue = mService.getProperty(propId, area);
255             return (propValue != null)
256                     && (propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE);
257         } catch (RemoteException e) {
258             Log.e(mTag, "isPropertyAvailable failed with " + e.toString()
259                     + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
260             throw new CarNotConnectedException(e);
261         }
262     }
263 
264     /**
265      * Returns value of a bool property
266      *
267      * @param prop Property ID to get
268      * @param area Area of the property to get
269      */
getBooleanProperty(int prop, int area)270     public boolean getBooleanProperty(int prop, int area) throws CarNotConnectedException {
271         CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, prop, area);
272         return carProp != null ? carProp.getValue() : false;
273     }
274 
275     /**
276      * Returns value of a float property
277      *
278      * @param prop Property ID to get
279      * @param area Area of the property to get
280      */
getFloatProperty(int prop, int area)281     public float getFloatProperty(int prop, int area) throws CarNotConnectedException {
282         CarPropertyValue<Float> carProp = getProperty(Float.class, prop, area);
283         return carProp != null ? carProp.getValue() : 0f;
284     }
285 
286     /**
287      * Returns value of a integer property
288      *
289      * @param prop Property ID to get
290      * @param area Zone of the property to get
291      */
getIntProperty(int prop, int area)292     public int getIntProperty(int prop, int area) throws CarNotConnectedException {
293         CarPropertyValue<Integer> carProp = getProperty(Integer.class, prop, area);
294         return carProp != null ? carProp.getValue() : 0;
295     }
296 
297     /** Return CarPropertyValue */
298     @SuppressWarnings("unchecked")
getProperty(Class<E> clazz, int propId, int area)299     public <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area)
300             throws CarNotConnectedException {
301         if (mDbg) {
302             Log.d(mTag, "getProperty, propId: 0x" + toHexString(propId)
303                     + ", area: 0x" + toHexString(area) + ", class: " + clazz);
304         }
305         try {
306             CarPropertyValue<E> propVal = mService.getProperty(propId, area);
307             if (propVal != null && propVal.getValue() != null) {
308                 Class<?> actualClass = propVal.getValue().getClass();
309                 if (actualClass != clazz) {
310                     throw new IllegalArgumentException("Invalid property type. " + "Expected: "
311                             + clazz + ", but was: " + actualClass);
312                 }
313             }
314             return propVal;
315         } catch (RemoteException e) {
316             Log.e(mTag, "getProperty failed with " + e.toString()
317                     + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
318             throw new CarNotConnectedException(e);
319         }
320     }
321 
322     /** Return raw CarPropertyValue */
getProperty(int propId, int area)323     public <E> CarPropertyValue<E> getProperty(int propId, int area)
324             throws CarNotConnectedException {
325         try {
326             CarPropertyValue<E> propVal = mService.getProperty(propId, area);
327             return propVal;
328         } catch (RemoteException e) {
329             Log.e(mTag, "getProperty failed with " + e.toString()
330                     + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
331             throw new CarNotConnectedException(e);
332         }
333     }
334 
335     /** Set CarPropertyValue */
setProperty(Class<E> clazz, int propId, int area, E val)336     public <E> void setProperty(Class<E> clazz, int propId, int area, E val)
337             throws CarNotConnectedException {
338         if (mDbg) {
339             Log.d(mTag, "setProperty, propId: 0x" + toHexString(propId)
340                     + ", area: 0x" + toHexString(area) + ", class: " + clazz + ", val: " + val);
341         }
342         try {
343             mService.setProperty(new CarPropertyValue<>(propId, area, val));
344         } catch (RemoteException e) {
345             Log.e(mTag, "setProperty failed with " + e.toString(), e);
346             throw new CarNotConnectedException(e);
347         }
348     }
349 
350     /**
351      * Modifies a property.  If the property modification doesn't occur, an error event shall be
352      * generated and propagated back to the application.
353      *
354      * @param prop Property ID to modify
355      * @param area Area to apply the modification.
356      * @param val Value to set
357      */
setBooleanProperty(int prop, int area, boolean val)358     public void setBooleanProperty(int prop, int area, boolean val)
359             throws CarNotConnectedException {
360         setProperty(Boolean.class, prop, area, val);
361     }
362 
363     /** Set float value of property*/
setFloatProperty(int prop, int area, float val)364     public void setFloatProperty(int prop, int area, float val) throws CarNotConnectedException {
365         setProperty(Float.class, prop, area, val);
366     }
367     /** Set int value of property*/
setIntProperty(int prop, int area, int val)368     public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException {
369         setProperty(Integer.class, prop, area, val);
370     }
371 
372 
373     private class CarPropertyListeners extends CarRatedFloatListeners<CarPropertyEventListener> {
CarPropertyListeners(float rate)374         CarPropertyListeners(float rate) {
375             super(rate);
376         }
onPropertyChanged(final CarPropertyEvent event)377         void onPropertyChanged(final CarPropertyEvent event) {
378             // throw away old sensor data as oneway binder call can change order.
379             long updateTime = event.getCarPropertyValue().getTimestamp();
380             if (updateTime < mLastUpdateTime) {
381                 Log.w(mTag, "dropping old property data");
382                 return;
383             }
384             mLastUpdateTime = updateTime;
385             List<CarPropertyEventListener> listeners;
386             synchronized (mActivePropertyListener) {
387                 listeners = new ArrayList<>(getListeners());
388             }
389             listeners.forEach(new Consumer<CarPropertyEventListener>() {
390                 @Override
391                 public void accept(CarPropertyEventListener listener) {
392                     listener.onChangeEvent(event.getCarPropertyValue());
393                 }
394             });
395         }
396 
onErrorEvent(final CarPropertyEvent event)397         void onErrorEvent(final CarPropertyEvent event) {
398             List<CarPropertyEventListener> listeners;
399             CarPropertyValue value = event.getCarPropertyValue();
400             synchronized (mActivePropertyListener) {
401                 listeners = new ArrayList<>(getListeners());
402             }
403             listeners.forEach(new Consumer<CarPropertyEventListener>() {
404                 @Override
405                 public void accept(CarPropertyEventListener listener) {
406                     listener.onErrorEvent(value.getPropertyId(), value.getAreaId());
407                 }
408             });
409         }
410     }
411 
412     /** @hide */
413     @Override
onCarDisconnected()414     public void onCarDisconnected() {
415         synchronized (mActivePropertyListener) {
416             mActivePropertyListener.clear();
417             mCarPropertyEventToService = null;
418         }
419     }
420 }
421