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
17package android.car.hardware;
18
19import android.annotation.Nullable;
20import android.annotation.SystemApi;
21import android.car.VehicleAreaType;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.util.SparseArray;
25
26import java.lang.reflect.Array;
27import java.util.ArrayList;
28import java.util.Collections;
29import java.util.List;
30
31/**
32 * Represents general information about car property such as data type and min/max ranges for car
33 * areas (if applicable). This class supposed to be immutable, parcelable and could be passed over.
34 *
35 * <p>Use {@link CarPropertyConfig#newBuilder} to create an instance of this class.
36 *
37 * @param <T> refer to Parcel#writeValue(Object) to get a list of all supported types. The class
38 * should be visible to framework as default class loader is being used here.
39 *
40 * @hide
41 */
42@SystemApi
43public class CarPropertyConfig<T> implements Parcelable {
44    private final int mAccess;
45    private final int mAreaType;
46    private final int mChangeMode;
47    private final ArrayList<Integer> mConfigArray;
48    private final String mConfigString;
49    private final float mMaxSampleRate;
50    private final float mMinSampleRate;
51    private final int mPropertyId;
52    private final SparseArray<AreaConfig<T>> mSupportedAreas;
53    private final Class<T> mType;
54
55    private CarPropertyConfig(int access, int areaType, int changeMode,
56            ArrayList<Integer> configArray, String configString,
57            float maxSampleRate, float minSampleRate, int propertyId,
58            SparseArray<AreaConfig<T>> supportedAreas, Class<T> type) {
59        mAccess = access;
60        mAreaType = areaType;
61        mChangeMode = changeMode;
62        mConfigArray = configArray;
63        mConfigString = configString;
64        mMaxSampleRate = maxSampleRate;
65        mMinSampleRate = minSampleRate;
66        mPropertyId = propertyId;
67        mSupportedAreas = supportedAreas;
68        mType = type;
69    }
70
71    public int getAccess() {
72        return mAccess;
73    }
74    public @VehicleAreaType.VehicleAreaTypeValue int getAreaType() {
75        return mAreaType;
76    }
77    public int getChangeMode() {
78        return mChangeMode;
79    }
80    public List<Integer> getConfigArray() {
81        return Collections.unmodifiableList(mConfigArray);
82    }
83    public String getConfigString() {
84        return mConfigString;
85    }
86    public float getMaxSampleRate() {
87        return mMaxSampleRate;
88    }
89    public float getMinSampleRate() {
90        return mMinSampleRate;
91    }
92    public int getPropertyId() {
93        return mPropertyId;
94    }
95    public Class<T> getPropertyType() {
96        return mType;
97    }
98
99    /** Returns true if this property doesn't hold car area-specific configuration */
100    public boolean isGlobalProperty() {
101        return mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
102    }
103
104    public int getAreaCount() {
105        return mSupportedAreas.size();
106    }
107
108    public int[] getAreaIds() {
109        int[] areaIds = new int[mSupportedAreas.size()];
110        for (int i = 0; i < areaIds.length; i++) {
111            areaIds[i] = mSupportedAreas.keyAt(i);
112        }
113        return areaIds;
114    }
115
116    /**
117     * Returns the first areaId.
118     * Throws {@link IllegalStateException} if supported area count not equals to one.
119     * */
120    public int getFirstAndOnlyAreaId() {
121        if (mSupportedAreas.size() != 1) {
122            throw new IllegalStateException("Expected one and only area in this property. Prop: 0x"
123                    + Integer.toHexString(mPropertyId));
124        }
125        return mSupportedAreas.keyAt(0);
126    }
127
128    public boolean hasArea(int areaId) {
129        return mSupportedAreas.indexOfKey(areaId) >= 0;
130    }
131
132    @Nullable
133    public T getMinValue(int areaId) {
134        AreaConfig<T> area = mSupportedAreas.get(areaId);
135        return area == null ? null : area.getMinValue();
136    }
137
138    @Nullable
139    public T getMaxValue(int areaId) {
140        AreaConfig<T> area = mSupportedAreas.get(areaId);
141        return area == null ? null : area.getMaxValue();
142    }
143
144    @Nullable
145    public T getMinValue() {
146        AreaConfig<T> area = mSupportedAreas.valueAt(0);
147        return area == null ? null : area.getMinValue();
148    }
149
150    @Nullable
151    public T getMaxValue() {
152        AreaConfig<T> area = mSupportedAreas.valueAt(0);
153        return area == null ? null : area.getMaxValue();
154    }
155
156    @Override
157    public int describeContents() {
158        return 0;
159    }
160
161    @Override
162    public void writeToParcel(Parcel dest, int flags) {
163        dest.writeInt(mAccess);
164        dest.writeInt(mAreaType);
165        dest.writeInt(mChangeMode);
166        dest.writeInt(mConfigArray.size());
167        for (int i = 0; i < mConfigArray.size(); i++) {
168            dest.writeInt(mConfigArray.get(i));
169        }
170        dest.writeString(mConfigString);
171        dest.writeFloat(mMaxSampleRate);
172        dest.writeFloat(mMinSampleRate);
173        dest.writeInt(mPropertyId);
174        dest.writeInt(mSupportedAreas.size());
175        for (int i = 0; i < mSupportedAreas.size(); i++) {
176            dest.writeInt(mSupportedAreas.keyAt(i));
177            dest.writeParcelable(mSupportedAreas.valueAt(i), flags);
178        }
179        dest.writeString(mType.getName());
180    }
181
182    @SuppressWarnings("unchecked")
183    private CarPropertyConfig(Parcel in) {
184        mAccess = in.readInt();
185        mAreaType = in.readInt();
186        mChangeMode = in.readInt();
187        int configArraySize = in.readInt();
188        mConfigArray = new ArrayList<Integer>(configArraySize);
189        for (int i = 0; i < configArraySize; i++) {
190            mConfigArray.add(in.readInt());
191        }
192        mConfigString = in.readString();
193        mMaxSampleRate = in.readFloat();
194        mMinSampleRate = in.readFloat();
195        mPropertyId = in.readInt();
196        int areaSize = in.readInt();
197        mSupportedAreas = new SparseArray<>(areaSize);
198        for (int i = 0; i < areaSize; i++) {
199            int areaId = in.readInt();
200            AreaConfig<T> area = in.readParcelable(getClass().getClassLoader());
201            mSupportedAreas.put(areaId, area);
202        }
203        String className = in.readString();
204        try {
205            mType = (Class<T>) Class.forName(className);
206        } catch (ClassNotFoundException e) {
207            throw new IllegalArgumentException("Class not found: " + className);
208        }
209    }
210
211    public static final Creator<CarPropertyConfig> CREATOR = new Creator<CarPropertyConfig>() {
212        @Override
213        public CarPropertyConfig createFromParcel(Parcel in) {
214            return new CarPropertyConfig(in);
215        }
216
217        @Override
218        public CarPropertyConfig[] newArray(int size) {
219            return new CarPropertyConfig[size];
220        }
221    };
222
223    @Override
224    public String toString() {
225        return "CarPropertyConfig{"
226                + "mPropertyId=" + mPropertyId
227                + ", mAccess=" + mAccess
228                + ", mAreaType=" + mAreaType
229                + ", mChangeMode=" + mChangeMode
230                + ", mConfigArray=" + mConfigArray
231                + ", mConfigString=" + mConfigString
232                + ", mMaxSampleRate=" + mMaxSampleRate
233                + ", mMinSampleRate=" + mMinSampleRate
234                + ", mSupportedAreas=" + mSupportedAreas
235                + ", mType=" + mType
236                + '}';
237    }
238
239    public static class AreaConfig<T> implements Parcelable {
240        @Nullable private final T mMinValue;
241        @Nullable private final T mMaxValue;
242
243        private AreaConfig(T minValue, T maxValue) {
244            mMinValue = minValue;
245            mMaxValue = maxValue;
246        }
247
248        public static final Parcelable.Creator<AreaConfig<Object>> CREATOR
249                = getCreator(Object.class);
250
251        private static <E> Parcelable.Creator<AreaConfig<E>> getCreator(final Class<E> clazz) {
252            return new Creator<AreaConfig<E>>() {
253                @Override
254                public AreaConfig<E> createFromParcel(Parcel source) {
255                    return new AreaConfig<>(source);
256                }
257
258                @Override @SuppressWarnings("unchecked")
259                public AreaConfig<E>[] newArray(int size) {
260                    return (AreaConfig<E>[]) Array.newInstance(clazz, size);
261                }
262            };
263        }
264
265        @SuppressWarnings("unchecked")
266        private AreaConfig(Parcel in) {
267            mMinValue = (T) in.readValue(getClass().getClassLoader());
268            mMaxValue = (T) in.readValue(getClass().getClassLoader());
269        }
270
271        @Nullable public T getMinValue() { return mMinValue; }
272        @Nullable public T getMaxValue() { return mMaxValue; }
273
274        @Override
275        public int describeContents() {
276            return 0;
277        }
278
279        @Override
280        public void writeToParcel(Parcel dest, int flags) {
281            dest.writeValue(mMinValue);
282            dest.writeValue(mMaxValue);
283        }
284
285        @Override
286        public String toString() {
287            return "CarAreaConfig{" +
288                    "mMinValue=" + mMinValue +
289                    ", mMaxValue=" + mMaxValue +
290                    '}';
291        }
292    }
293
294    /**
295     * Prepare an instance of CarPropertyConfig
296     *
297     * @return Builder<T>
298     */
299    public static <T> Builder<T> newBuilder(Class<T> type, int propertyId, int areaType,
300                                            int areaCapacity) {
301        return new Builder<>(areaCapacity, areaType, propertyId, type);
302    }
303
304
305    /**
306     * Prepare an instance of CarPropertyConfig
307     *
308     * @return Builder<T>
309     */
310    public static <T> Builder<T> newBuilder(Class<T> type, int propertyId, int areaType) {
311        return new Builder<>(0, areaType, propertyId, type);
312    }
313
314    public static class Builder<T> {
315        private int mAccess;
316        private final int mAreaType;
317        private int mChangeMode;
318        private final ArrayList<Integer> mConfigArray;
319        private String mConfigString;
320        private float mMaxSampleRate;
321        private float mMinSampleRate;
322        private final int mPropertyId;
323        private final SparseArray<AreaConfig<T>> mSupportedAreas;
324        private final Class<T> mType;
325
326        private Builder(int areaCapacity, int areaType, int propertyId, Class<T> type) {
327            mAreaType = areaType;
328            mConfigArray = new ArrayList<>();
329            mPropertyId = propertyId;
330            if (areaCapacity != 0) {
331                mSupportedAreas = new SparseArray<>(areaCapacity);
332            } else {
333                mSupportedAreas = new SparseArray<>();
334            }
335            mType = type;
336        }
337
338        /**
339         * Add supported areas parameter to CarPropertyConfig
340         *
341         * @return Builder<T>
342         */
343        public Builder<T> addAreas(int[] areaIds) {
344            for (int id : areaIds) {
345                mSupportedAreas.put(id, null);
346            }
347            return this;
348        }
349
350        /**
351         * Add area to CarPropertyConfig
352         *
353         * @return Builder<T>
354         */
355        public Builder<T> addArea(int areaId) {
356            return addAreaConfig(areaId, null, null);
357        }
358
359        /**
360         * Add areaConfig to CarPropertyConfig
361         *
362         * @return Builder<T>
363         */
364        public Builder<T> addAreaConfig(int areaId, T min, T max) {
365            if (min == null && max == null) {
366                mSupportedAreas.put(areaId, null);
367            } else {
368                mSupportedAreas.put(areaId, new AreaConfig<>(min, max));
369            }
370            return this;
371        }
372
373        /**
374         * Set access parameter to CarPropertyConfig
375         *
376         * @return Builder<T>
377         */
378        public Builder<T> setAccess(int access) {
379            mAccess = access;
380            return this;
381        }
382
383        /**
384         * Set changeMode parameter to CarPropertyConfig
385         *
386         * @return Builder<T>
387         */
388        public Builder<T> setChangeMode(int changeMode) {
389            mChangeMode = changeMode;
390            return this;
391        }
392
393        /**
394         * Set configArray parameter to CarPropertyConfig
395         *
396         * @return Builder<T>
397         */
398        public Builder<T> setConfigArray(ArrayList<Integer> configArray) {
399            mConfigArray.clear();
400            mConfigArray.addAll(configArray);
401            return this;
402        }
403
404        /**
405         * Set configString parameter to CarPropertyConfig
406         *
407         * @return Builder<T>
408         */
409        public Builder<T> setConfigString(String configString) {
410            mConfigString = configString;
411            return this;
412        }
413
414        /**
415         * Set maxSampleRate parameter to CarPropertyConfig
416         *
417         * @return Builder<T>
418         */
419        public Builder<T> setMaxSampleRate(float maxSampleRate) {
420            mMaxSampleRate = maxSampleRate;
421            return this;
422        }
423
424        /**
425         * Set minSampleRate parameter to CarPropertyConfig
426         *
427         * @return Builder<T>
428         */
429        public Builder<T> setMinSampleRate(float minSampleRate) {
430            mMinSampleRate = minSampleRate;
431            return this;
432        }
433
434        public CarPropertyConfig<T> build() {
435            return new CarPropertyConfig<>(mAccess, mAreaType, mChangeMode, mConfigArray,
436                                           mConfigString, mMaxSampleRate, mMinSampleRate,
437                                           mPropertyId, mSupportedAreas, mType);
438        }
439    }
440}
441