CarSensorManager.java revision 4afc6ee52690326316e2ed1b7bc354f466849426
1/*
2 * Copyright (C) 2015 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
17package android.car.hardware;
18
19import android.Manifest;
20import android.annotation.RequiresPermission;
21import android.car.Car;
22import android.car.CarApiUtil;
23import android.car.CarLibLog;
24import android.car.CarManagerBase;
25import android.car.CarNotConnectedException;
26import android.content.Context;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Looper;
30import android.os.Message;
31import android.os.RemoteException;
32import android.os.Handler.Callback;
33import android.util.Log;
34
35import java.lang.ref.WeakReference;
36import java.util.HashMap;
37import java.util.Iterator;
38import java.util.LinkedList;
39import java.util.List;
40
41/**
42 *  API for monitoring car sensor data.
43 */
44public class CarSensorManager implements CarManagerBase {
45    /** @hide */
46    public static final int SENSOR_TYPE_RESERVED1           = 1;
47    /**
48     * This sensor represents vehicle speed in m/s.
49     * Sensor data in {@link CarSensorEvent} is a float which will be >= 0.
50     * This requires {@link Car#PERMISSION_SPEED} permission.
51     */
52    public static final int SENSOR_TYPE_CAR_SPEED         = 2;
53    /**
54     * Represents engine RPM of the car. Sensor data in {@link CarSensorEvent} is a float.
55     */
56    public static final int SENSOR_TYPE_RPM               = 3;
57    /**
58     * Total travel distance of the car in Kilometer. Sensor data is a float.
59     * This requires {@link Car#PERMISSION_MILEAGE} permission.
60     */
61    public static final int SENSOR_TYPE_ODOMETER          = 4;
62    /**
63     * Indicates fuel level of the car.
64     * In {@link CarSensorEvent}, floatValues[{@link CarSensorEvent#INDEX_FUEL_LEVEL_IN_PERCENTILE}]
65     * represents fuel level in percentile (0 to 100) while
66     * floatValues[{@link CarSensorEvent#INDEX_FUEL_LEVEL_IN_DISTANCE}] represents estimated range
67     * in Kilometer with the remaining fuel.
68     * Note that the gas mileage used for the estimation may not represent the current driving
69     * condition.
70     * This requires {@link Car#PERMISSION_FUEL} permission.
71     */
72    public static final int SENSOR_TYPE_FUEL_LEVEL        = 5;
73    /**
74     * Represents the current status of parking brake. Sensor data in {@link CarSensorEvent} is an
75     * intValues[0]. Value of 1 represents parking brake applied while 0 means the other way
76     * around. For this sensor, rate in {@link #registerListener(CarSensorEventListener, int, int)}
77     * will be ignored and all changes will be notified.
78     */
79    public static final int SENSOR_TYPE_PARKING_BRAKE     = 6;
80    /**
81     * This represents the current position of transmission gear. Sensor data in
82     * {@link CarSensorEvent} is an intValues[0]. For the meaning of the value, check
83     * {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
84     */
85    public static final int SENSOR_TYPE_GEAR              = 7;
86    /**
87     * Day/night sensor. Sensor data is intValues[0].
88     */
89    public static final int SENSOR_TYPE_NIGHT             = 8;
90    /** @hide */
91    public static final int SENSOR_TYPE_REVERVED9        = 9;
92    /**
93     * Represents the current driving status of car. Different user interaction should be used
94     * depending on the current driving status. Driving status is intValues[0].
95     */
96    public static final int SENSOR_TYPE_DRIVING_STATUS    = 10;
97    /**
98     * Environment like temperature and pressure.
99     */
100    public static final int SENSOR_TYPE_ENVIRONMENT       = 11;
101    /** @hide */
102    public static final int SENSOR_TYPE_RESERVED12        = 12;
103    /** @hide */
104    public static final int SENSOR_TYPE_RESERVED13        = 13;
105    /** @hide */
106    public static final int SENSOR_TYPE_RESERVED14        = 14;
107
108    /**
109     * Sensor type bigger than this is invalid. Always update this after adding a new sensor.
110     * @hide
111     */
112    private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_ENVIRONMENT;
113
114    /**
115     * Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
116     * {@link #SENSOR_TYPE_VENDOR_EXTENSION_END}] is for each car vendor's to use.
117     * This should be only used for system app to access sensors not defined as standard types.
118     * So the sensor supported in this range can vary depending on car models / manufacturers.
119     * 3rd party apps should not use sensors in this range as they are not compatible across
120     * different cars. Additionally 3rd party apps trying to access sensor in this range will get
121     * security exception as their access is restricted to system apps.
122     *
123     * @hide
124     */
125    public static final int SENSOR_TYPE_VENDOR_EXTENSION_START = 0x60000000;
126    public static final int SENSOR_TYPE_VENDOR_EXTENSION_END   = 0x6fffffff;
127
128    /** Read sensor in default normal rate set for each sensors. This is default rate. */
129    public static final int SENSOR_RATE_NORMAL  = 3;
130    public static final int SENSOR_RATE_UI = 2;
131    public static final int SENSOR_RATE_FAST = 1;
132    /** Read sensor at the maximum rate. Actual rate will be different depending on the sensor. */
133    public static final int SENSOR_RATE_FASTEST = 0;
134
135    private static final int MSG_SENSOR_EVENTS = 0;
136
137    private final ICarSensor mService;
138
139    private CarSensorEventListenerToService mCarSensorEventListenerToService;
140
141    /**
142     * To keep record of locally active sensors. Key is sensor type. This is used as a basic lock
143     * for all client accesses.
144     */
145    private final HashMap<Integer, CarSensorListeners> mActiveSensorListeners =
146            new HashMap<Integer, CarSensorListeners>();
147
148    /** Handles call back into projected apps. */
149    private final Handler mHandler;
150    private final Callback mHandlerCallback = new Callback() {
151        @Override
152        public boolean handleMessage(Message msg) {
153            switch (msg.what) {
154                case MSG_SENSOR_EVENTS:
155                    synchronized(mActiveSensorListeners) {
156                        List<CarSensorEvent> events = (List<CarSensorEvent>) msg.obj;
157                        for (CarSensorEvent event: events) {
158                            CarSensorListeners listeners =
159                                    mActiveSensorListeners.get(event.sensorType);
160                            if (listeners != null) {
161                                listeners.onSensorChanged(event);
162                            }
163                        }
164                    }
165                    break;
166                default:
167                    break;
168            }
169            return true;
170        }
171    };
172
173
174    /** @hide */
175    public CarSensorManager(IBinder service, Context context, Looper looper) {
176        mService = ICarSensor.Stub.asInterface(service);
177        mHandler = new Handler(looper, mHandlerCallback);
178    }
179
180    /** @hide */
181    @Override
182    public void onCarDisconnected() {
183        synchronized(mActiveSensorListeners) {
184            mActiveSensorListeners.clear();
185            mCarSensorEventListenerToService = null;
186        }
187    }
188
189    /**
190     * Give the list of CarSensors available in the connected car.
191     * @return array of all sensor types supported.
192     * @throws CarNotConnectedException
193     */
194    public int[] getSupportedSensors() throws CarNotConnectedException {
195        try {
196            return mService.getSupportedSensors();
197        } catch (IllegalStateException e) {
198            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
199        } catch (RemoteException e) {
200            //ignore
201        }
202        return new int[0];
203    }
204
205    /**
206     * Tells if given sensor is supported or not.
207     * @param sensorType
208     * @return true if the sensor is supported.
209     * @throws CarNotConnectedException
210     */
211    public boolean isSensorSupported(int sensorType) throws CarNotConnectedException {
212        int[] sensors = getSupportedSensors();
213        for (int sensorSupported: sensors) {
214            if (sensorType == sensorSupported) {
215                return true;
216            }
217        }
218        return false;
219    }
220
221    /**
222     * Check if given sensorList is including the sensorType.
223     * @param sensorList
224     * @param sensorType
225     * @return
226     */
227    public static boolean isSensorSupported(int[] sensorList, int sensorType) {
228        for (int sensorSupported: sensorList) {
229            if (sensorType == sensorSupported) {
230                return true;
231            }
232        }
233        return false;
234    }
235
236    /**
237     * Listener for car sensor data change.
238     * Callbacks are called in the Looper context.
239     */
240    public interface CarSensorEventListener {
241        /**
242         * Called when there is a new sensor data from car.
243         * @param event Incoming sensor event for the given sensor type.
244         */
245        void onSensorChanged(final CarSensorEvent event);
246    }
247
248    /**
249     * Register {@link CarSensorEventListener} to get repeated sensor updates. Multiple listeners
250     * can be registered for a single sensor or the same listener can be used for different sensors.
251     * If the same listener is registered again for the same sensor, it will be either ignored or
252     * updated depending on the rate.
253     * <p>
254     * Requires {@link Car#PERMISSION_SPEED} for {@link #SENSOR_TYPE_CAR_SPEED},
255     *  {@link Car#PERMISSION_MILEAGE} for {@link #SENSOR_TYPE_ODOMETER},
256     *  or {@link Car#PERMISSION_FUEL} for {@link #SENSOR_TYPE_FUEL_LEVEL}.
257     *
258     * @param listener
259     * @param sensorType sensor type to subscribe.
260     * @param rate how fast the sensor events are delivered. It should be one of
261     *        {@link #SENSOR_RATE_FASTEST} or {@link #SENSOR_RATE_NORMAL}. Rate may not be respected
262     *        especially when the same sensor is registered with different listener with different
263     *        rates.
264     * @return if the sensor was successfully enabled.
265     * @throws CarNotConnectedException
266     * @throws IllegalArgumentException for wrong argument like wrong rate
267     * @throws SecurityException if missing the appropriate permission
268     */
269    @RequiresPermission(anyOf={Manifest.permission.ACCESS_FINE_LOCATION, Car.PERMISSION_SPEED,
270            Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL}, conditional=true)
271    public boolean registerListener(CarSensorEventListener listener, int sensorType, int rate)
272            throws CarNotConnectedException, IllegalArgumentException {
273        assertSensorType(sensorType);
274        if (rate != SENSOR_RATE_FASTEST && rate != SENSOR_RATE_NORMAL) {
275            throw new IllegalArgumentException("wrong rate " + rate);
276        }
277        synchronized(mActiveSensorListeners) {
278            if (mCarSensorEventListenerToService == null) {
279                mCarSensorEventListenerToService = new CarSensorEventListenerToService(this);
280            }
281            boolean needsServerUpdate = false;
282            CarSensorListeners listeners;
283            listeners = mActiveSensorListeners.get(sensorType);
284            if (listeners == null) {
285                listeners = new CarSensorListeners(rate);
286                mActiveSensorListeners.put(sensorType, listeners);
287                needsServerUpdate = true;
288            }
289            if (listeners.addAndUpdateRate(listener, rate)) {
290                needsServerUpdate = true;
291            }
292            if (needsServerUpdate) {
293                if (!registerOrUpdateSensorListener(sensorType, rate)) {
294                    return false;
295                }
296            }
297        }
298        return true;
299    }
300
301    /**
302     * Stop getting sensor update for the given listener. If there are multiple registrations for
303     * this listener, all listening will be stopped.
304     * @param listener
305     */
306    public void unregisterListener(CarSensorEventListener listener) {
307        //TODO: removing listener should reset update rate
308        synchronized(mActiveSensorListeners) {
309            Iterator<Integer> sensorIterator = mActiveSensorListeners.keySet().iterator();
310            while (sensorIterator.hasNext()) {
311                Integer sensor = sensorIterator.next();
312                doUnregisterListenerLocked(listener, sensor, sensorIterator);
313            }
314        }
315    }
316
317    /**
318     * Stop getting sensor update for the given listener and sensor. If the same listener is used
319     * for other sensors, those subscriptions will not be affected.
320     * @param listener
321     * @param sensorType
322     */
323    public void unregisterListener(CarSensorEventListener listener, int sensorType) {
324        synchronized(mActiveSensorListeners) {
325            doUnregisterListenerLocked(listener, sensorType, null);
326        }
327    }
328
329    private void doUnregisterListenerLocked(CarSensorEventListener listener, Integer sensor,
330            Iterator<Integer> sensorIterator) {
331        CarSensorListeners listeners = mActiveSensorListeners.get(sensor);
332        if (listeners != null) {
333            if (listeners.contains(listener)) {
334                listeners.remove(listener);
335            }
336            if (listeners.isEmpty()) {
337                try {
338                    mService.unregisterSensorListener(sensor.intValue(),
339                            mCarSensorEventListenerToService);
340                } catch (RemoteException e) {
341                    // ignore
342                }
343                if (sensorIterator == null) {
344                    mActiveSensorListeners.remove(sensor);
345                } else {
346                    sensorIterator.remove();
347                }
348            }
349        }
350    }
351
352    private boolean registerOrUpdateSensorListener(int sensor, int rate)
353            throws CarNotConnectedException {
354        try {
355            if (!mService.registerOrUpdateSensorListener(sensor, rate,
356                    mCarSensorEventListenerToService)) {
357                return false;
358            }
359        } catch (IllegalStateException e) {
360            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
361        } catch (RemoteException e) {
362            return false;
363        }
364        return true;
365    }
366
367    /**
368     * Get the most recent CarSensorEvent for the given type. Note that latest sensor data from car
369     * will not be available if it was never subscribed before. This call will return immediately
370     * with null if there is no data available.
371     * @param type A sensor to request
372     * @return null if there was no sensor update since connected to the car.
373     * @throws CarNotConnectedException
374     */
375    public CarSensorEvent getLatestSensorEvent(int type) throws CarNotConnectedException {
376        assertSensorType(type);
377        try {
378            return mService.getLatestSensorEvent(type);
379        } catch (IllegalStateException e) {
380            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
381        } catch(RemoteException e) {
382            handleCarServiceRemoteExceptionAndThrow(e);
383        }
384        return null;
385    }
386
387    private void handleCarServiceRemoteExceptionAndThrow(RemoteException e)
388            throws CarNotConnectedException {
389        if (Log.isLoggable(CarLibLog.TAG_SENSOR, Log.INFO)) {
390            Log.i(CarLibLog.TAG_SENSOR, "RemoteException from car service:" + e.getMessage());
391        }
392        throw new CarNotConnectedException();
393    }
394
395    private void assertSensorType(int sensorType) {
396        if (sensorType == 0 || !((sensorType <= SENSOR_TYPE_MAX) ||
397                ((sensorType >= SENSOR_TYPE_VENDOR_EXTENSION_START) &&
398                        (sensorType <= SENSOR_TYPE_VENDOR_EXTENSION_END)))) {
399            throw new IllegalArgumentException("invalid sensor type " + sensorType);
400        }
401    }
402
403    private void handleOnSensorChanged(List<CarSensorEvent> events) {
404        mHandler.sendMessage(mHandler.obtainMessage(MSG_SENSOR_EVENTS, events));
405    }
406
407    private static class CarSensorEventListenerToService extends ICarSensorEventListener.Stub {
408        private final WeakReference<CarSensorManager> mManager;
409
410        public CarSensorEventListenerToService(CarSensorManager manager) {
411            mManager = new WeakReference<CarSensorManager>(manager);
412        }
413
414        @Override
415        public void onSensorChanged(List<CarSensorEvent> events) {
416            CarSensorManager manager = mManager.get();
417            if (manager != null) {
418                manager.handleOnSensorChanged(events);
419            }
420        }
421    }
422
423    /**
424     * Represent listeners for a sensor.
425     */
426    private class CarSensorListeners {
427        private final LinkedList<CarSensorEventListener> mListeners =
428                new LinkedList<CarSensorEventListener>();
429
430        private int mUpdateRate;
431        private long mLastUpdateTime = -1;
432
433        CarSensorListeners(int rate) {
434            mUpdateRate = rate;
435        }
436
437        boolean contains(CarSensorEventListener listener) {
438            return mListeners.contains(listener);
439        }
440
441        void remove(CarSensorEventListener listener) {
442            mListeners.remove(listener);
443        }
444
445        boolean isEmpty() {
446            return mListeners.isEmpty();
447        }
448
449        /**
450         * Add given listener to the list and update rate if necessary.
451         * @param listener if null, add part is skipped.
452         * @param updateRate
453         * @return true if rate was updated. Otherwise, returns false.
454         */
455        boolean addAndUpdateRate(CarSensorEventListener listener, int updateRate) {
456            if (!mListeners.contains(listener)) {
457                mListeners.add(listener);
458            }
459            if (mUpdateRate > updateRate) {
460                mUpdateRate = updateRate;
461                return true;
462            }
463            return false;
464        }
465
466        void onSensorChanged(CarSensorEvent event) {
467            // throw away old sensor data as oneway binder call can change order.
468            long updateTime = event.timeStampNs;
469            if (updateTime < mLastUpdateTime) {
470                Log.w(CarLibLog.TAG_SENSOR, "dropping old sensor data");
471                return;
472            }
473            mLastUpdateTime = updateTime;
474            for (CarSensorEventListener listener: mListeners) {
475                listener.onSensorChanged(event);
476            }
477        }
478    }
479}
480