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.support.car.hardware;
18
19import android.location.GpsSatellite;
20import android.location.Location;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.os.SystemClock;
24import android.support.car.annotation.VersionDef;
25import android.support.car.os.ExtendableParcelable;
26
27
28/**
29 * A CarSensorEvent object corresponds to a single sensor event coming from the car. The sensor
30 * data is stored in a sensor-type specific format in the object's float and byte arrays.
31 *
32 * To aid unmarshalling the object's data arrays, this class provides static nested classes and
33 * conversion methods, for example {@link EnvironmentData} and {@link #getEnvironmentData}. The
34 * conversion methods each have an optional data parameter which, if not null, will be used and
35 * returned. This parameter should be used to avoid unnecessary object churn whenever possible.
36 * Additionally, calling a conversion method on a CarSensorEvent object with an inappropriate type
37 * will result in an {@code UnsupportedOperationException} being thrown.
38 */
39public class CarSensorEvent extends ExtendableParcelable {
40
41    private static final int VERSION = 1;
42
43    /**
44     * Index in {@link #floatValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
45     * sensor. This value is fuel level in percentile.
46     */
47    public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0;
48    /**
49     * Index in {@link #floatValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
50     * sensor. This value is fuel level in coverable distance. The unit is Km.
51     */
52    public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1;
53    /**
54     * Index in {@link #intValues} for {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL} type of
55     * sensor. This value is set to 1 if fuel low level warning is on.
56     */
57    public static final int INDEX_FUEL_LOW_WARNING = 0;
58
59    /**
60     *  GEAR_* represents meaning of intValues[0] for {@link CarSensorManager#SENSOR_TYPE_GEAR}
61     *  sensor type.
62     *  GEAR_NEUTRAL means transmission gear is in neutral state, and the car may be moving.
63     */
64    public static final int GEAR_NEUTRAL    = 0;
65    /**
66     * intValues[0] from 1 to 99 represents transmission gear number for moving forward.
67     * GEAR_FIRST is for gear number 1.
68     */
69    public static final int GEAR_FIRST      = 1;
70    /** Gear number 2. */
71    public static final int GEAR_SECOND     = 2;
72    /** Gear number 3. */
73    public static final int GEAR_THIRD      = 3;
74    /** Gear number 4. */
75    public static final int GEAR_FOURTH     = 4;
76    /** Gear number 5. */
77    public static final int GEAR_FIFTH      = 5;
78    /** Gear number 6. */
79    public static final int GEAR_SIXTH      = 6;
80    /** Gear number 7. */
81    public static final int GEAR_SEVENTH    = 7;
82    /** Gear number 8. */
83    public static final int GEAR_EIGHTH     = 8;
84    /** Gear number 9. */
85    public static final int GEAR_NINTH      = 9;
86    /** Gear number 10. */
87    public static final int GEAR_TENTH      = 10;
88    /**
89     * This is for transmission without specific gear number for moving forward like CVT. It tells
90     * that car is in a transmission state to move it forward.
91     */
92    public static final int GEAR_DRIVE      = 100;
93    /** Gear in parking state */
94    public static final int GEAR_PARK       = 101;
95    /** Gear in reverse */
96    public static final int GEAR_REVERSE    = 102;
97
98    /**
99     * Bitmask of driving restrictions.
100     */
101    /** No restrictions. */
102    public static final int DRIVE_STATUS_UNRESTRICTED = 0;
103    /** No video playback allowed. */
104    public static final int DRIVE_STATUS_NO_VIDEO = 0x1;
105    /** No keyboard or rotary controller input allowed. */
106    public static final int DRIVE_STATUS_NO_KEYBOARD_INPUT = 0x2;
107    /** No voice input allowed. */
108    public static final int DRIVE_STATUS_NO_VOICE_INPUT = 0x4;
109    /** No setup / configuration allowed. */
110    public static final int DRIVE_STATUS_NO_CONFIG = 0x8;
111    /** Limit displayed message length. */
112    public static final int DRIVE_STATUS_LIMIT_MESSAGE_LEN = 0x10;
113    /** represents case where all of the above items are restricted */
114    public static final int DRIVE_STATUS_FULLY_RESTRICTED = DRIVE_STATUS_NO_VIDEO |
115            DRIVE_STATUS_NO_KEYBOARD_INPUT | DRIVE_STATUS_NO_VOICE_INPUT | DRIVE_STATUS_NO_CONFIG |
116            DRIVE_STATUS_LIMIT_MESSAGE_LEN;
117    /**
118     * Index for {@link CarSensorManager#SENSOR_TYPE_LOCATION} in floatValues.
119     * Each bit intValues[0] represents whether the corresponding data is present.
120     */
121    public static final int INDEX_LOCATION_LATITUDE  = 0;
122    public static final int INDEX_LOCATION_LONGITUDE = 1;
123    public static final int INDEX_LOCATION_ACCURACY  = 2;
124    public static final int INDEX_LOCATION_ALTITUDE  = 3;
125    public static final int INDEX_LOCATION_SPEED     = 4;
126    public static final int INDEX_LOCATION_BEARING   = 5;
127    public static final int INDEX_LOCATION_MAX = INDEX_LOCATION_BEARING;
128    public static final int INDEX_LOCATION_LATITUDE_INTS = 1;
129    public static final int INDEX_LOCATION_LONGITUDE_INTS = 2;
130
131    /**
132     * Index for {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT} in floatValues.
133     * Temperature in Celsius degrees.
134     */
135    public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0;
136    /**
137     * Index for {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT} in floatValues.
138     * Pressure in kPa.
139     */
140    public static final int INDEX_ENVIRONMENT_PRESSURE = 1;
141
142    /**
143     * Indices for {@link CarSensorManager#SENSOR_TYPE_COMPASS} in floatValues.
144     * Angles are in degrees. Pitch or/and roll can be NaN if it is not available.
145     */
146    public static final int INDEX_COMPASS_BEARING = 0;
147    public static final int INDEX_COMPASS_PITCH   = 1;
148    public static final int INDEX_COMPASS_ROLL    = 2;
149
150    /**
151     * Indices for {@link CarSensorManager#SENSOR_TYPE_ACCELEROMETER} in floatValues.
152     * Acceleration (gravity) is in m/s^2. Any component can be NaN if it is not available.
153     */
154    public static final int INDEX_ACCELEROMETER_X = 0;
155    public static final int INDEX_ACCELEROMETER_Y = 1;
156    public static final int INDEX_ACCELEROMETER_Z = 2;
157
158    /**
159     * Indices for {@link CarSensorManager#SENSOR_TYPE_GYROSCOPE} in floatValues.
160     * Rotation speed is in rad/s. Any component can be NaN if it is not available.
161     */
162    public static final int INDEX_GYROSCOPE_X = 0;
163    public static final int INDEX_GYROSCOPE_Y = 1;
164    public static final int INDEX_GYROSCOPE_Z = 2;
165
166    /**
167     * Indices for {@link CarSensorManager#SENSOR_TYPE_GPS_SATELLITE}.
168     * Both byte values and float values are used.
169     * Two first bytes encode number of satellites in-use/in-view (or 0xFF if unavailable).
170     * Then optionally with INDEX_GPS_SATELLITE_ARRAY_BYTE_OFFSET offset and interval
171     * INDEX_GPS_SATELLITE_ARRAY_BYTE_INTERVAL between elements are encoded boolean flags of whether
172     * particular satellite from in-view participate in in-use subset.
173     * Float values with INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET offset and interval
174     * INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL between elements can optionally contain
175     * per-satellite values of signal strength and other values or NaN if unavailable.
176     */
177    public static final int INDEX_GPS_SATELLITE_NUMBER_IN_USE = 0;
178    public static final int INDEX_GPS_SATELLITE_NUMBER_IN_VIEW = 1;
179    public static final int INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET = 2;
180    public static final int INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL = 1;
181    public static final int INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET = 0;
182    public static final int INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL = 4;
183    public static final int INDEX_GPS_SATELLITE_PRN_OFFSET = 0;
184    public static final int INDEX_GPS_SATELLITE_SNR_OFFSET = 1;
185    public static final int INDEX_GPS_SATELLITE_AZIMUTH_OFFSET = 2;
186    public static final int INDEX_GPS_SATELLITE_ELEVATION_OFFSET = 3;
187
188    private static final long MILLI_IN_NANOS = 1000000L;
189
190    /** Sensor type for this event like {@link CarSensorManager#SENSOR_TYPE_CAR_SPEED}. */
191    @VersionDef(version = 1)
192    public int sensorType;
193
194    /**
195     * When this data was acquired in car or received from car. It is elapsed real-time of data
196     * reception from car in nanoseconds since system boot.
197     */
198    @VersionDef(version = 1)
199    public long timeStampNs;
200    /**
201     * array holding float type of sensor data. If the sensor has single value, only floatValues[0]
202     * should be used. */
203    @VersionDef(version = 1)
204    public final float[] floatValues;
205    /** array holding int type of sensor data */
206    @VersionDef(version = 1)
207    public final int[] intValues;
208
209    public CarSensorEvent(Parcel in) {
210        super(in, VERSION);
211        int lastPosition = readHeader(in);
212        sensorType = in.readInt();
213        timeStampNs = in.readLong();
214        int len = in.readInt();
215        floatValues = new float[len];
216        in.readFloatArray(floatValues);
217        len = in.readInt();
218        intValues = new int[len];
219        in.readIntArray(intValues);
220        // version 1 up to here
221        completeReading(in, lastPosition);
222    }
223
224    @Override
225    public int describeContents() {
226        return 0;
227    }
228
229    @Override
230    public void writeToParcel(Parcel dest, int flags) {
231        int startingPosition = writeHeader(dest);
232        dest.writeInt(sensorType);
233        dest.writeLong(timeStampNs);
234        dest.writeInt(floatValues.length);
235        dest.writeFloatArray(floatValues);
236        dest.writeInt(intValues.length);
237        dest.writeIntArray(intValues);
238        // version 1 up to here
239        completeWriting(dest, startingPosition);
240    }
241
242    public static final Parcelable.Creator<CarSensorEvent> CREATOR
243    = new Parcelable.Creator<CarSensorEvent>() {
244        public CarSensorEvent createFromParcel(Parcel in) {
245            return new CarSensorEvent(in);
246        }
247
248        public CarSensorEvent[] newArray(int size) {
249            return new CarSensorEvent[size];
250        }
251    };
252
253    public CarSensorEvent(int sensorType, long timeStampNs, int floatValueSize, int intValueSize) {
254        super(VERSION);
255        this.sensorType = sensorType;
256        this.timeStampNs = timeStampNs;
257        floatValues = new float[floatValueSize];
258        intValues = new int[intValueSize];
259    }
260
261    /** @hide */
262    CarSensorEvent(int sensorType, long timeStampNs, float[] floatValues, int[] intValues) {
263        super(VERSION);
264        this.sensorType = sensorType;
265        this.timeStampNs = timeStampNs;
266        this.floatValues = floatValues;
267        this.intValues = intValues;
268    }
269
270    private void checkType(int type) {
271        if (sensorType == type) {
272            return;
273        }
274        throw new UnsupportedOperationException(String.format(
275                "Invalid sensor type: expected %d, got %d", type, sensorType));
276    }
277
278    public static class EnvironmentData {
279        public long timeStampNs;
280        /** If unsupported by the car, this value is NaN. */
281        public float temperature;
282        /** If unsupported by the car, this value is NaN. */
283        public float pressure;
284    }
285
286    /**
287     * Convenience method for obtaining an {@link EnvironmentData} object from a CarSensorEvent
288     * object with type {@link CarSensorManager#SENSOR_TYPE_ENVIRONMENT}.
289     *
290     * @param data an optional output parameter which, if non-null, will be used by this method
291     *     instead of a newly created object.
292     * @return an EnvironmentData object corresponding to the data contained in the CarSensorEvent.
293     */
294    public EnvironmentData getEnvironmentData(EnvironmentData data) {
295        checkType(CarSensorManager.SENSOR_TYPE_ENVIRONMENT);
296        if (data == null) {
297            data = new EnvironmentData();
298        }
299        data.timeStampNs = timeStampNs;
300        data.temperature = floatValues[INDEX_ENVIRONMENT_TEMPERATURE];
301        data.pressure = floatValues[INDEX_ENVIRONMENT_PRESSURE];
302        return data;
303    }
304
305    public static class NightData {
306        public long timeStampNs;
307        public boolean isNightMode;
308    }
309
310    /**
311     * Convenience method for obtaining a {@link NightData} object from a CarSensorEvent
312     * object with type {@link CarSensorManager#SENSOR_TYPE_NIGHT}.
313     *
314     * @param data an optional output parameter which, if non-null, will be used by this method
315     *     instead of a newly created object.
316     * @return a NightData object corresponding to the data contained in the CarSensorEvent.
317     */
318    public NightData getNightData(NightData data) {
319        checkType(CarSensorManager.SENSOR_TYPE_NIGHT);
320        if (data == null) {
321            data = new NightData();
322        }
323        data.timeStampNs = timeStampNs;
324        data.isNightMode = intValues[0] == 1;
325        return data;
326    }
327
328    public static class GearData {
329        public long timeStampNs;
330        public int gear;
331    }
332
333    /**
334     * Convenience method for obtaining a {@link GearData} object from a CarSensorEvent
335     * object with type {@link CarSensorManager#SENSOR_TYPE_GEAR}.
336     *
337     * @param data an optional output parameter which, if non-null, will be used by this method
338     *     instead of a newly created object.
339     * @return a GearData object corresponding to the data contained in the CarSensorEvent.
340     */
341    public GearData getGearData(GearData data) {
342        checkType(CarSensorManager.SENSOR_TYPE_GEAR);
343        if (data == null) {
344            data = new GearData();
345        }
346        data.timeStampNs = timeStampNs;
347        data.gear = intValues[0];
348        return data;
349    }
350
351    public static class ParkingBrakeData {
352        public long timeStampNs;
353        public boolean isEngaged;
354    }
355
356    /**
357     * Convenience method for obtaining a {@link ParkingBrakeData} object from a CarSensorEvent
358     * object with type {@link CarSensorManager#SENSOR_TYPE_PARKING_BRAKE}.
359     *
360     * @param data an optional output parameter which, if non-null, will be used by this method
361     *     instead of a newly created object.
362     * @return a ParkingBreakData object corresponding to the data contained in the CarSensorEvent.
363     */
364    public ParkingBrakeData getParkingBrakeData(ParkingBrakeData data) {
365        checkType(CarSensorManager.SENSOR_TYPE_PARKING_BRAKE);
366        if (data == null) {
367            data = new ParkingBrakeData();
368        }
369        data.timeStampNs = timeStampNs;
370        data.isEngaged = intValues[0] == 1;
371        return data;
372    }
373
374    public static class FuelLevelData {
375        public long timeStampNs;
376        /** Fuel level in %. If unsupported by the car, this value is -1. */
377        public int level;
378        /** Fuel as possible range in Km. If unsupported by the car, this value is -1. */
379        public float range;
380        /** If unsupported by the car, this value is false. */
381        public boolean lowFuelWarning;
382    }
383
384    /**
385     * Convenience method for obtaining a {@link FuelLevelData} object from a CarSensorEvent
386     * object with type {@link CarSensorManager#SENSOR_TYPE_FUEL_LEVEL}.
387     *
388     * @param data an optional output parameter which, if non-null, will be used by this method
389     *     instead of a newly created object.
390     * @return a FuelLevel object corresponding to the data contained in the CarSensorEvent.
391     */
392    public FuelLevelData getFuelLevelData(FuelLevelData data) {
393        checkType(CarSensorManager.SENSOR_TYPE_FUEL_LEVEL);
394        if (data == null) {
395            data = new FuelLevelData();
396        }
397        data.timeStampNs = timeStampNs;
398        if (floatValues == null) {
399            data.level = -1;
400            data.range = -1;
401        } else {
402            if (floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE] < 0) {
403                data.level = -1;
404            } else {
405                data.level = (int) floatValues[INDEX_FUEL_LEVEL_IN_PERCENTILE];
406            }
407            if (floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE] < 0) {
408                data.range = -1;
409            } else {
410                data.range = floatValues[INDEX_FUEL_LEVEL_IN_DISTANCE];
411            }
412        }
413        data.lowFuelWarning = intValues[0] == 1;
414        return data;
415    }
416
417    public static class OdometerData {
418        public long timeStampNs;
419        public float kms;
420    }
421
422    /**
423     * Convenience method for obtaining an {@link OdometerData} object from a CarSensorEvent
424     * object with type {@link CarSensorManager#SENSOR_TYPE_ODOMETER}.
425     *
426     * @param data an optional output parameter which, if non-null, will be used by this method
427     *     instead of a newly created object.
428     * @return an OdometerData object corresponding to the data contained in the CarSensorEvent.
429     */
430    public OdometerData getOdometerData(OdometerData data) {
431        checkType(CarSensorManager.SENSOR_TYPE_ODOMETER);
432        if (data == null) {
433            data = new OdometerData();
434        }
435        data.timeStampNs = timeStampNs;
436        data.kms = floatValues[0];
437        return data;
438    }
439
440    public static class RpmData {
441        public long timeStampNs;
442        public float rpm;
443    }
444
445    /**
446     * Convenience method for obtaining a {@link RpmData} object from a CarSensorEvent
447     * object with type {@link CarSensorManager#SENSOR_TYPE_RPM}.
448     *
449     * @param data an optional output parameter which, if non-null, will be used by this method
450     *     instead of a newly created object.
451     * @return a RpmData object corresponding to the data contained in the CarSensorEvent.
452     */
453    public RpmData getRpmData(RpmData data) {
454        checkType(CarSensorManager.SENSOR_TYPE_RPM);
455        if (data == null) {
456            data = new RpmData();
457        }
458        data.timeStampNs = timeStampNs;
459        data.rpm = floatValues[0];
460        return data;
461    }
462
463    public static class CarSpeedData {
464        public long timeStampNs;
465        public float carSpeed;
466    }
467
468    /**
469     * Convenience method for obtaining a {@link CarSpeedData} object from a CarSensorEvent
470     * object with type {@link CarSensorManager#SENSOR_TYPE_CAR_SPEED}.
471     *
472     * @param data an optional output parameter which, if non-null, will be used by this method
473     *     instead of a newly created object.
474     * @return a CarSpeedData object corresponding to the data contained in the CarSensorEvent.
475     */
476    public CarSpeedData getCarSpeedData(CarSpeedData data) {
477        checkType(CarSensorManager.SENSOR_TYPE_CAR_SPEED);
478        if (data == null) {
479            data = new CarSpeedData();
480        }
481        data.timeStampNs = timeStampNs;
482        data.carSpeed = floatValues[0];
483        return data;
484    }
485
486    public static class CompassData {
487        public long timeStampNs;
488        /** If unsupported by the car, this value is NaN. */
489        public float bearing;
490        /** If unsupported by the car, this value is NaN. */
491        public float pitch;
492        /** If unsupported by the car, this value is NaN. */
493        public float roll;
494    }
495
496    /**
497     * Convenience method for obtaining a {@link CompassData} object from a CarSensorEvent
498     * object with type {@link CarSensorManager#SENSOR_TYPE_COMPASS}.
499     *
500     * @param data an optional output parameter which, if non-null, will be used by this method
501     *     instead of a newly created object.
502     * @return a CompassData object corresponding to the data contained in the CarSensorEvent.
503     */
504    public CompassData getCompassData(CompassData data) {
505        checkType(CarSensorManager.SENSOR_TYPE_COMPASS);
506        if (data == null) {
507            data = new CompassData();
508        }
509        data.bearing = floatValues[INDEX_COMPASS_BEARING];
510        data.pitch = floatValues[INDEX_COMPASS_PITCH];
511        data.roll = floatValues[INDEX_COMPASS_ROLL];
512        return data;
513    }
514
515    /**
516     * Convenience method for obtaining a {@link Location} object from a CarSensorEvent
517     * object with type {@link CarSensorManager#SENSOR_TYPE_LOCATION}.
518     *
519     * @param location an optional output parameter which, if non-null, will be used by this method
520     *     instead of a newly created object.
521     * @return a Location object corresponding to the data contained in the CarSensorEvent.
522     */
523    public Location getLocation(Location location) {
524        checkType(CarSensorManager.SENSOR_TYPE_LOCATION);
525        if (location == null) {
526            location = new Location("Car-GPS");
527        }
528        // intValues[0]: bit flags for the presence of other values following.
529        int presense = intValues[0];
530        if ((presense & (0x1 << INDEX_LOCATION_LATITUDE)) != 0) {
531            int latE7 = intValues[INDEX_LOCATION_LATITUDE_INTS];
532            location.setLatitude(latE7 * 1e-7);
533        }
534        if ((presense & (0x1 << INDEX_LOCATION_LONGITUDE)) != 0) {
535            int longE7 = intValues[INDEX_LOCATION_LONGITUDE_INTS];
536            location.setLongitude(longE7 * 1e-7);
537        }
538        if ((presense & (0x1 << INDEX_LOCATION_ACCURACY)) != 0) {
539            location.setAccuracy(floatValues[INDEX_LOCATION_ACCURACY]);
540        }
541        if ((presense & (0x1 << INDEX_LOCATION_ALTITUDE)) != 0) {
542            location.setAltitude(floatValues[INDEX_LOCATION_ALTITUDE]);
543        }
544        if ((presense & (0x1 << INDEX_LOCATION_SPEED)) != 0) {
545            location.setSpeed(floatValues[INDEX_LOCATION_SPEED]);
546        }
547        if ((presense & (0x1 << INDEX_LOCATION_BEARING)) != 0) {
548            location.setBearing(floatValues[INDEX_LOCATION_BEARING]);
549        }
550        location.setElapsedRealtimeNanos(timeStampNs);
551        // There is a risk of scheduler delaying 2nd elapsedRealtimeNs value.
552        // But will not try to fix it assuming that is acceptable as UTC time's accuracy is not
553        // guaranteed in Location data.
554        long currentTimeMs = System.currentTimeMillis();
555        long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
556        location.setTime(
557                currentTimeMs - (elapsedRealtimeNs - timeStampNs) / MILLI_IN_NANOS);
558        return location;
559    }
560
561    public static class DrivingStatusData {
562        public long timeStampNs;
563        public int status;
564    }
565
566    /**
567     * Convenience method for obtaining a {@link DrivingStatusData} object from a CarSensorEvent
568     * object with type {@link CarSensorManager#SENSOR_TYPE_DRIVING_STATUS}.
569     *
570     * @param data an optional output parameter which, if non-null, will be used by this method
571     *     instead of a newly created object.
572     * @return a DrivingStatusData object corresponding to the data contained in the CarSensorEvent.
573     */
574    public DrivingStatusData getDrivingStatusData(DrivingStatusData data) {
575        checkType(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS);
576        if (data == null) {
577            data = new DrivingStatusData();
578        }
579        data.status = intValues[0];
580        return data;
581    }
582
583    public static class AccelerometerData  {
584        public long timeStampNs;
585        /** If unsupported by the car, this value is NaN. */
586        public float x;
587        /** If unsupported by the car, this value is NaN. */
588        public float y;
589        /** If unsupported by the car, this value is NaN. */
590        public float z;
591    }
592
593    /**
594     * Convenience method for obtaining an {@link AccelerometerData} object from a CarSensorEvent
595     * object with type {@link CarSensorManager#SENSOR_TYPE_ACCELEROMETER}.
596     *
597     * @param data an optional output parameter which, if non-null, will be used by this method
598     *     instead of a newly created object.
599     * @return a AccelerometerData object corresponding to the data contained in the CarSensorEvent.
600     */
601    public AccelerometerData getAccelerometerData(AccelerometerData data) {
602        checkType(CarSensorManager.SENSOR_TYPE_ACCELEROMETER);
603        if (data == null) {
604            data = new AccelerometerData();
605        }
606        data.x = floatValues[INDEX_ACCELEROMETER_X];
607        data.y = floatValues[INDEX_ACCELEROMETER_Y];
608        data.z = floatValues[INDEX_ACCELEROMETER_Z];
609        return data;
610    }
611
612    public static class GyroscopeData {
613        public long timeStampNs;
614        /** If unsupported by the car, this value is NaN. */
615        public float x;
616        /** If unsupported by the car, this value is NaN. */
617        public float y;
618        /** If unsupported by the car, this value is NaN. */
619        public float z;
620    }
621
622    /**
623     * Convenience method for obtaining a {@link GyroscopeData} object from a CarSensorEvent
624     * object with type {@link CarSensorManager#SENSOR_TYPE_GYROSCOPE}.
625     *
626     * @param data an optional output parameter which, if non-null, will be used by this method
627     *     instead of a newly created object.
628     * @return a GyroscopeData object corresponding to the data contained in the CarSensorEvent.
629     */
630    public GyroscopeData getGyroscopeData(GyroscopeData data) {
631        checkType(CarSensorManager.SENSOR_TYPE_GYROSCOPE);
632        if (data == null) {
633            data = new GyroscopeData();
634        }
635        data.x = floatValues[INDEX_GYROSCOPE_X];
636        data.y = floatValues[INDEX_GYROSCOPE_Y];
637        data.z = floatValues[INDEX_GYROSCOPE_Z];
638        return data;
639    }
640
641    // android.location.GpsSatellite doesn't have a public constructor, so that can't be used.
642    /**
643     * Class that contains GPS satellite status. For more info on meaning of these fields refer
644     * to the documentation to the {@link GpsSatellite} class.
645     */
646    public static class GpsSatelliteData {
647        public long timeStampNs;
648        /**
649         * Number of satellites used in GPS fix or -1 of unavailable.
650         */
651        public int numberInUse = -1;
652        /**
653         * Number of satellites in view or -1 of unavailable.
654         */
655        public int numberInView = -1;
656        /**
657         * Per-satellite flag if this satellite was used for GPS fix.
658         * Can be null if per-satellite data is unavailable.
659         */
660        public boolean[] usedInFix = null;
661        /**
662         * Per-satellite pseudo-random id.
663         * Can be null if per-satellite data is unavailable.
664         */
665        public int[] prn = null;
666        /**
667         * Per-satellite signal to noise ratio.
668         * Can be null if per-satellite data is unavailable.
669         */
670        public float[] snr = null;
671        /**
672         * Per-satellite azimuth.
673         * Can be null if per-satellite data is unavailable.
674         */
675        public float[] azimuth = null;
676        /**
677         * Per-satellite elevation.
678         * Can be null if per-satellite data is unavailable.
679         */
680        public float[] elevation = null;
681    }
682
683    /**
684     * Convenience method for obtaining a {@link GpsSatelliteData} object from a CarSensorEvent
685     * object with type {@link CarSensorManager#SENSOR_TYPE_HVAC} with optional per-satellite info.
686     *
687     * @param data an optional output parameter which, if non-null, will be used by this method
688     *     instead of a newly created object.
689     * @param withPerSatellite whether to include per-satellite data.
690     * @return a GpsSatelliteData object corresponding to the data contained in the CarSensorEvent.
691     */
692    public GpsSatelliteData getGpsSatelliteData(GpsSatelliteData data,
693            boolean withPerSatellite) {
694        checkType(CarSensorManager.SENSOR_TYPE_GPS_SATELLITE);
695        if (data == null) {
696            data = new GpsSatelliteData();
697        }
698        final int intOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET;
699        final int intInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL;
700        final int floatOffset = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_FLOAT_OFFSET;
701        final int floatInterval = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_FLOAT_INTERVAL;
702        final int numberOfSats = (floatValues.length - floatOffset) / floatInterval;
703
704        data.numberInUse = intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_USE];
705        data.numberInView = intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_VIEW];
706        if (withPerSatellite && data.numberInView >= 0) {
707            data.usedInFix = new boolean[numberOfSats];
708            data.prn = new int[numberOfSats];
709            data.snr = new float[numberOfSats];
710            data.azimuth = new float[numberOfSats];
711            data.elevation = new float[numberOfSats];
712
713            for (int i = 0; i < numberOfSats; ++i) {
714                int iInt = intOffset + intInterval * i;
715                int iFloat = floatOffset + floatInterval * i;
716                data.usedInFix[i] = intValues[iInt] != 0;
717                data.prn[i] = Math.round(
718                        floatValues[iFloat + CarSensorEvent.INDEX_GPS_SATELLITE_PRN_OFFSET]);
719                data.snr[i] =
720                        floatValues[iFloat + CarSensorEvent.INDEX_GPS_SATELLITE_SNR_OFFSET];
721                data.azimuth[i] = floatValues[iFloat
722                        + CarSensorEvent.INDEX_GPS_SATELLITE_AZIMUTH_OFFSET];
723                data.elevation[i] = floatValues[iFloat
724                        + CarSensorEvent.INDEX_GPS_SATELLITE_ELEVATION_OFFSET];
725            }
726        }
727        return data;
728    }
729
730    /** @hide */
731    @Override
732    public String toString() {
733        StringBuilder sb = new StringBuilder();
734        sb.append(getClass().getName() + "[");
735        sb.append("type:" + Integer.toHexString(sensorType));
736        if (floatValues != null && floatValues.length > 0) {
737            sb.append(" float values:");
738            for (float v: floatValues) {
739                sb.append(" " + v);
740            }
741        }
742        if (intValues != null && intValues.length > 0) {
743            sb.append(" int values:");
744            for (int v: intValues) {
745                sb.append(" " + v);
746            }
747        }
748        sb.append("]");
749        return sb.toString();
750    }
751}
752