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