1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.car.hardware;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.car.VehicleAreaType;
24 import android.car.VehicleAreaType.VehicleAreaTypeValue;
25 import android.car.VehiclePropertyType;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.util.SparseArray;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.lang.reflect.Array;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 
37 /**
38  * Represents general information about car property such as data type and min/max ranges for car
39  * areas (if applicable). This class supposed to be immutable, parcelable and could be passed over.
40  *
41  * <p>Use {@link CarPropertyConfig#newBuilder} to create an instance of this class.
42  *
43  * @param <T> refer to Parcel#writeValue(Object) to get a list of all supported types. The class
44  * should be visible to framework as default class loader is being used here.
45  *
46  */
47 public final class CarPropertyConfig<T> implements Parcelable {
48     private final int mAccess;
49     private final int mAreaType;
50     private final int mChangeMode;
51     private final ArrayList<Integer> mConfigArray;
52     private final String mConfigString;
53     private final float mMaxSampleRate;
54     private final float mMinSampleRate;
55     private final int mPropertyId;
56     private final SparseArray<AreaConfig<T>> mSupportedAreas;
57     private final Class<T> mType;
58 
CarPropertyConfig(int access, int areaType, int changeMode, ArrayList<Integer> configArray, String configString, float maxSampleRate, float minSampleRate, int propertyId, SparseArray<AreaConfig<T>> supportedAreas, Class<T> type)59     private CarPropertyConfig(int access, int areaType, int changeMode,
60             ArrayList<Integer> configArray, String configString,
61             float maxSampleRate, float minSampleRate, int propertyId,
62             SparseArray<AreaConfig<T>> supportedAreas, Class<T> type) {
63         mAccess = access;
64         mAreaType = areaType;
65         mChangeMode = changeMode;
66         mConfigArray = configArray;
67         mConfigString = configString;
68         mMaxSampleRate = maxSampleRate;
69         mMinSampleRate = minSampleRate;
70         mPropertyId = propertyId;
71         mSupportedAreas = supportedAreas;
72         mType = type;
73     }
74 
75     /** @hide */
76     @IntDef(prefix = {"VEHICLE_PROPERTY_ACCESS"}, value = {
77         VEHICLE_PROPERTY_ACCESS_NONE,
78         VEHICLE_PROPERTY_ACCESS_READ,
79         VEHICLE_PROPERTY_ACCESS_WRITE,
80         VEHICLE_PROPERTY_ACCESS_READ_WRITE
81     })
82     @Retention(RetentionPolicy.SOURCE)
83     public @interface VehiclePropertyAccessType {}
84 
85     /** Property Access Unknown */
86     public static final int VEHICLE_PROPERTY_ACCESS_NONE = 0;
87     /** The property is readable */
88     public static final int VEHICLE_PROPERTY_ACCESS_READ = 1;
89     /** The property is writable */
90     public static final int VEHICLE_PROPERTY_ACCESS_WRITE = 2;
91     /** The property is readable and writable */
92     public static final int VEHICLE_PROPERTY_ACCESS_READ_WRITE = 3;
93 
94     /** @hide */
95     @IntDef(prefix = {"VEHICLE_PROPERTY_CHANGE_MODE"}, value = {
96         VEHICLE_PROPERTY_CHANGE_MODE_STATIC,
97         VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
98         VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS,
99     })
100     @Retention(RetentionPolicy.SOURCE)
101     public @interface VehiclePropertyChangeModeType {}
102 
103     /** Properties of this type must never be changed. */
104     public static final int VEHICLE_PROPERTY_CHANGE_MODE_STATIC = 0;
105     /** Properties of this type must report when there is a change. */
106     public static final int VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE = 1;
107     /** Properties of this type change continuously. */
108     public static final int VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS = 2;
109 
110     /**
111      * Return the access type of the car property.
112      * <p>The access type could be one of the following:
113      * <ul>
114      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_NONE}</li>
115      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ}</li>
116      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_WRITE}</li>
117      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_ACCESS_READ_WRITE}</li>
118      * </ul>
119      *
120      * @return the access type of the car property.
121      */
getAccess()122     public @VehiclePropertyAccessType int getAccess() {
123         return mAccess;
124     }
125 
126     /**
127      * Return the area type of the car property.
128      * <p>The area type could be one of the following:
129      * <ul>
130      *   <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL}</li>
131      *   <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_WINDOW}</li>
132      *   <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_SEAT}</li>
133      *   <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_DOOR}</li>
134      *   <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_MIRROR}</li>
135      *   <li>{@link VehicleAreaType#VEHICLE_AREA_TYPE_WHEEL}</li>
136      * </ul>
137      *
138      * @return the area type of the car property.
139      */
getAreaType()140     public @VehicleAreaTypeValue int getAreaType() {
141         return mAreaType;
142     }
143 
144     /**
145      * Return the change mode of the car property.
146      *
147      * <p>The change mode could be one of the following:
148      * <ul>
149      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_STATIC }</li>
150      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE}</li>
151      *   <li>{@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS}</li>
152      * </ul>
153      *
154      * @return the change mode of properties.
155      */
getChangeMode()156     public @VehiclePropertyChangeModeType int getChangeMode() {
157         return mChangeMode;
158     }
159 
160     /**
161      *
162      * @return Additional configuration parameters. For different properties, configArrays have
163      * different information.
164      */
165     @NonNull
getConfigArray()166     public List<Integer> getConfigArray() {
167         return Collections.unmodifiableList(mConfigArray);
168     }
169 
170     /**
171      *
172      * @return Some properties may require additional information passed over this
173      * string. Most properties do not need to set this.
174      * @hide
175      */
getConfigString()176     public String getConfigString() {
177         return mConfigString;
178     }
179 
180     /**
181      *
182      * @return Max sample rate in Hz. Must be defined for VehiclePropertyChangeMode::CONTINUOUS
183      * return 0 if change mode is not continuous.
184      */
getMaxSampleRate()185     public float getMaxSampleRate() {
186         return mMaxSampleRate;
187     }
188 
189     /**
190      *
191      * @return Min sample rate in Hz.Must be defined for VehiclePropertyChangeMode::CONTINUOUS
192      * return 0 if change mode is not continuous.
193      */
getMinSampleRate()194     public float getMinSampleRate() {
195         return mMinSampleRate;
196     }
197 
198     /**
199      * @return Property identifier
200      */
getPropertyId()201     public int getPropertyId() {
202         return mPropertyId;
203     }
204 
205     /**
206      * Returns the value type of the vehicle property.
207      * <p>The value type could be one of the following:
208      * <ul>
209      *   <li>Boolean</li>
210      *   <li>Float</li>
211      *   <li>Float[]</li>
212      *   <li>Integer</li>
213      *   <li>Integer[]</li>
214      *   <li>Long</li>
215      *   <li>Long[]</li>
216      *   <li>String</li>
217      *   <li>byte[]</li>
218      *   <li>Object[]</li>
219      * </ul>
220      *
221      * @return the value type of the vehicle property.
222      */
223     @NonNull
getPropertyType()224     public Class<T> getPropertyType() {
225         return mType;
226     }
227 
228     /**
229      *
230      * @return true if this property doesn't hold car area-specific configuration.
231      */
isGlobalProperty()232     public boolean isGlobalProperty() {
233         return mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
234     }
235 
236     /**
237      *
238      * @return the number of areaIds for properties.
239      * @hide
240      */
getAreaCount()241     public int getAreaCount() {
242         return mSupportedAreas.size();
243     }
244 
245     /**
246      *
247      * @return Array of areaIds. An AreaID is a combination of one or more areas,
248      * and is represented using a bitmask of Area enums. Different AreaTypes may
249      * not be mixed in a single AreaID. For instance, a window area cannot be
250      * combined with a seat area in an AreaID.
251      * Rules for mapping a zoned property to AreaIDs:
252      *  - A property must be mapped to an array of AreaIDs that are impacted when
253      *    the property value changes.
254      *  - Each element in the array must represent an AreaID, in which, the
255      *    property value can only be changed together in all the areas within
256      *    an AreaID and never independently. That is, when the property value
257      *    changes in one of the areas in an AreaID in the array, then it must
258      *    automatically change in all other areas in the AreaID.
259      *  - The property value must be independently controllable in any two
260      *    different AreaIDs in the array.
261      *  - An area must only appear once in the array of AreaIDs. That is, an
262      *    area must only be part of a single AreaID in the array.
263      */
264     @NonNull
getAreaIds()265     public int[] getAreaIds() {
266         int[] areaIds = new int[mSupportedAreas.size()];
267         for (int i = 0; i < areaIds.length; i++) {
268             areaIds[i] = mSupportedAreas.keyAt(i);
269         }
270         return areaIds;
271     }
272 
273     /**
274      * @return  the first areaId.
275      * Throws {@link IllegalStateException} if supported area count not equals to one.
276      * @hide
277      */
getFirstAndOnlyAreaId()278     public int getFirstAndOnlyAreaId() {
279         if (mSupportedAreas.size() != 1) {
280             throw new IllegalStateException("Expected one and only area in this property. Prop: 0x"
281                     + Integer.toHexString(mPropertyId));
282         }
283         return mSupportedAreas.keyAt(0);
284     }
285 
286     /**
287      *
288      * @param areaId
289      * @return true if areaId is existing.
290      * @hide
291      */
hasArea(int areaId)292     public boolean hasArea(int areaId) {
293         return mSupportedAreas.indexOfKey(areaId) >= 0;
294     }
295 
296     /**
297      *
298      * @param areaId
299      * @return Min value in given areaId. Null if not have min value in given area.
300      */
301     @Nullable
getMinValue(int areaId)302     public T getMinValue(int areaId) {
303         AreaConfig<T> area = mSupportedAreas.get(areaId);
304         return area == null ? null : area.getMinValue();
305     }
306 
307     /**
308      *
309      * @param areaId
310      * @return Max value in given areaId. Null if not have max value in given area.
311      */
312     @Nullable
getMaxValue(int areaId)313     public T getMaxValue(int areaId) {
314         AreaConfig<T> area = mSupportedAreas.get(areaId);
315         return area == null ? null : area.getMaxValue();
316     }
317 
318     /**
319      *
320      * @return Min value in areaId 0. Null if not have min value.
321      */
322     @Nullable
getMinValue()323     public T getMinValue() {
324         AreaConfig<T> area = mSupportedAreas.get(0);
325         return area == null ? null : area.getMinValue();
326     }
327 
328     /**
329      *
330      * @return Max value in areaId 0. Null if not have max value.
331      */
332     @Nullable
getMaxValue()333     public T getMaxValue() {
334         AreaConfig<T> area = mSupportedAreas.get(0);
335         return area == null ? null : area.getMaxValue();
336     }
337 
338     @Override
describeContents()339     public int describeContents() {
340         return 0;
341     }
342 
343 
344     @Override
writeToParcel(Parcel dest, int flags)345     public void writeToParcel(Parcel dest, int flags) {
346         dest.writeInt(mAccess);
347         dest.writeInt(mAreaType);
348         dest.writeInt(mChangeMode);
349         dest.writeInt(mConfigArray.size());
350         for (int i = 0; i < mConfigArray.size(); i++) {
351             dest.writeInt(mConfigArray.get(i));
352         }
353         dest.writeString(mConfigString);
354         dest.writeFloat(mMaxSampleRate);
355         dest.writeFloat(mMinSampleRate);
356         dest.writeInt(mPropertyId);
357         dest.writeInt(mSupportedAreas.size());
358         for (int i = 0; i < mSupportedAreas.size(); i++) {
359             dest.writeInt(mSupportedAreas.keyAt(i));
360             dest.writeParcelable(mSupportedAreas.valueAt(i), flags);
361         }
362         dest.writeString(mType.getName());
363     }
364 
365     @SuppressWarnings("unchecked")
CarPropertyConfig(Parcel in)366     private CarPropertyConfig(Parcel in) {
367         mAccess = in.readInt();
368         mAreaType = in.readInt();
369         mChangeMode = in.readInt();
370         int configArraySize = in.readInt();
371         mConfigArray = new ArrayList<Integer>(configArraySize);
372         for (int i = 0; i < configArraySize; i++) {
373             mConfigArray.add(in.readInt());
374         }
375         mConfigString = in.readString();
376         mMaxSampleRate = in.readFloat();
377         mMinSampleRate = in.readFloat();
378         mPropertyId = in.readInt();
379         int areaSize = in.readInt();
380         mSupportedAreas = new SparseArray<>(areaSize);
381         for (int i = 0; i < areaSize; i++) {
382             int areaId = in.readInt();
383             AreaConfig<T> area = in.readParcelable(getClass().getClassLoader());
384             mSupportedAreas.put(areaId, area);
385         }
386         String className = in.readString();
387         try {
388             mType = (Class<T>) Class.forName(className);
389         } catch (ClassNotFoundException e) {
390             throw new IllegalArgumentException("Class not found: " + className);
391         }
392     }
393 
394     public static final Creator<CarPropertyConfig> CREATOR = new Creator<CarPropertyConfig>() {
395         @Override
396         public CarPropertyConfig createFromParcel(Parcel in) {
397             return new CarPropertyConfig(in);
398         }
399 
400         @Override
401         public CarPropertyConfig[] newArray(int size) {
402             return new CarPropertyConfig[size];
403         }
404     };
405 
406     /** @hide */
407     @Override
toString()408     public String toString() {
409         return "CarPropertyConfig{"
410                 + "mPropertyId=" + mPropertyId
411                 + ", mAccess=" + mAccess
412                 + ", mAreaType=" + mAreaType
413                 + ", mChangeMode=" + mChangeMode
414                 + ", mConfigArray=" + mConfigArray
415                 + ", mConfigString=" + mConfigString
416                 + ", mMaxSampleRate=" + mMaxSampleRate
417                 + ", mMinSampleRate=" + mMinSampleRate
418                 + ", mSupportedAreas=" + mSupportedAreas
419                 + ", mType=" + mType
420                 + '}';
421     }
422 
423     /**
424      * Represents min/max value of car property.
425      * @param <T>
426      * @hide
427      */
428     public static class AreaConfig<T> implements Parcelable {
429         @Nullable private final T mMinValue;
430         @Nullable private final T mMaxValue;
431 
AreaConfig(T minValue, T maxValue)432         private AreaConfig(T minValue, T maxValue) {
433             mMinValue = minValue;
434             mMaxValue = maxValue;
435         }
436 
437         public static final Parcelable.Creator<AreaConfig<Object>> CREATOR =
438                 getCreator(Object.class);
439 
getCreator(final Class<E> clazz)440         private static <E> Parcelable.Creator<AreaConfig<E>> getCreator(final Class<E> clazz) {
441             return new Creator<AreaConfig<E>>() {
442                 @Override
443                 public AreaConfig<E> createFromParcel(Parcel source) {
444                     return new AreaConfig<>(source);
445                 }
446 
447                 @Override @SuppressWarnings("unchecked")
448                 public AreaConfig<E>[] newArray(int size) {
449                     return (AreaConfig<E>[]) Array.newInstance(clazz, size);
450                 }
451             };
452         }
453 
454         @SuppressWarnings("unchecked")
AreaConfig(Parcel in)455         private AreaConfig(Parcel in) {
456             mMinValue = (T) in.readValue(getClass().getClassLoader());
457             mMaxValue = (T) in.readValue(getClass().getClassLoader());
458         }
459 
getMinValue()460         @Nullable public T getMinValue() {
461             return mMinValue;
462         }
463 
getMaxValue()464         @Nullable public T getMaxValue() {
465             return mMaxValue;
466         }
467 
468         @Override
describeContents()469         public int describeContents() {
470             return 0;
471         }
472 
473         @Override
writeToParcel(Parcel dest, int flags)474         public void writeToParcel(Parcel dest, int flags) {
475             dest.writeValue(mMinValue);
476             dest.writeValue(mMaxValue);
477         }
478 
479         @Override
toString()480         public String toString() {
481             return "CarAreaConfig{"
482                     + "mMinValue=" + mMinValue
483                     + ", mMaxValue=" + mMaxValue
484                     + '}';
485         }
486     }
487 
488     /**
489      * Prepare an instance of CarPropertyConfig
490      *
491      * @return Builder<T>
492      * @hide
493      */
494     @SystemApi
495     public static <T> Builder<T> newBuilder(Class<T> type, int propertyId, int areaType,
496                                             int areaCapacity) {
497         return new Builder<>(areaCapacity, areaType, propertyId, type);
498     }
499 
500 
501     /**
502      * Prepare an instance of CarPropertyConfig
503      *
504      * @return Builder<T>
505      * @hide
506      */
507     public static <T> Builder<T> newBuilder(Class<T> type, int propertyId, int areaType) {
508         return new Builder<>(0, areaType, propertyId, type);
509     }
510 
511 
512     /**
513      * @param <T>
514      * @hide
515      * */
516     @SystemApi
517     public static class Builder<T> {
518         private int mAccess;
519         private final int mAreaType;
520         private int mChangeMode;
521         private final ArrayList<Integer> mConfigArray;
522         private String mConfigString;
523         private float mMaxSampleRate;
524         private float mMinSampleRate;
525         private final int mPropertyId;
526         private final SparseArray<AreaConfig<T>> mSupportedAreas;
527         private final Class<T> mType;
528 
529         private Builder(int areaCapacity, int areaType, int propertyId, Class<T> type) {
530             mAreaType = areaType;
531             mConfigArray = new ArrayList<>();
532             mPropertyId = propertyId;
533             if (areaCapacity != 0) {
534                 mSupportedAreas = new SparseArray<>(areaCapacity);
535             } else {
536                 mSupportedAreas = new SparseArray<>();
537             }
538             mType = type;
539         }
540 
541         /**
542          * Add supported areas parameter to CarPropertyConfig
543          *
544          * @return Builder<T>
545          */
546         public Builder<T> addAreas(int[] areaIds) {
547             for (int id : areaIds) {
548                 mSupportedAreas.put(id, null);
549             }
550             return this;
551         }
552 
553         /**
554          * Add area to CarPropertyConfig
555          *
556          * @return Builder<T>
557          */
558         public Builder<T> addArea(int areaId) {
559             return addAreaConfig(areaId, null, null);
560         }
561 
562         /**
563          * Add areaConfig to CarPropertyConfig
564          *
565          * @return Builder<T>
566          */
567         public Builder<T> addAreaConfig(int areaId, T min, T max) {
568             if (!isRangeAvailable(min, max)) {
569                 mSupportedAreas.put(areaId, null);
570             } else {
571                 mSupportedAreas.put(areaId, new AreaConfig<>(min, max));
572             }
573             return this;
574         }
575 
576         /**
577          * Set access parameter to CarPropertyConfig
578          *
579          * @return Builder<T>
580          */
581         public Builder<T> setAccess(int access) {
582             mAccess = access;
583             return this;
584         }
585 
586         /**
587          * Set changeMode parameter to CarPropertyConfig
588          *
589          * @return Builder<T>
590          */
591         public Builder<T> setChangeMode(int changeMode) {
592             mChangeMode = changeMode;
593             return this;
594         }
595 
596         /**
597          * Set configArray parameter to CarPropertyConfig
598          *
599          * @return Builder<T>
600          */
601         public Builder<T> setConfigArray(ArrayList<Integer> configArray) {
602             mConfigArray.clear();
603             mConfigArray.addAll(configArray);
604             return this;
605         }
606 
607         /**
608          * Set configString parameter to CarPropertyConfig
609          *
610          * @return Builder<T>
611          */
612         public Builder<T> setConfigString(String configString) {
613             mConfigString = configString;
614             return this;
615         }
616 
617         /**
618          * Set maxSampleRate parameter to CarPropertyConfig
619          *
620          * @return Builder<T>
621          */
622         public Builder<T> setMaxSampleRate(float maxSampleRate) {
623             mMaxSampleRate = maxSampleRate;
624             return this;
625         }
626 
627         /**
628          * Set minSampleRate parameter to CarPropertyConfig
629          *
630          * @return Builder<T>
631          */
632         public Builder<T> setMinSampleRate(float minSampleRate) {
633             mMinSampleRate = minSampleRate;
634             return this;
635         }
636 
637         /**
638          * Builds a new {@link CarPropertyConfig}.
639          */
640         public CarPropertyConfig<T> build() {
641             return new CarPropertyConfig<>(mAccess, mAreaType, mChangeMode, mConfigArray,
642                                            mConfigString, mMaxSampleRate, mMinSampleRate,
643                                            mPropertyId, mSupportedAreas, mType);
644         }
645 
646         private boolean isRangeAvailable(T min, T max) {
647             if (min == null || max == null) {
648                 return false;
649             }
650             int propertyType = mPropertyId & VehiclePropertyType.MASK;
651             switch (propertyType) {
652                 case VehiclePropertyType.INT32:
653                     return (Integer) min  != 0 || (Integer) max != 0;
654                 case VehiclePropertyType.INT64:
655                     return (Long) min != 0L || (Long) max != 0L;
656                 case VehiclePropertyType.FLOAT:
657                     return (Float) min != 0f || (Float) max != 0f;
658                 default:
659                     return false;
660             }
661         }
662     }
663 }
664