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