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 static java.lang.Integer.toHexString;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.nio.charset.Charset;
29 import java.nio.charset.StandardCharsets;
30 
31 /**
32  * Stores values broken down by area for a vehicle property.
33  *
34  * @param <T> refer to Parcel#writeValue(Object) to get a list of all supported types. The class
35  * should be visible to framework as default class loader is being used here.
36  *
37  */
38 public final class CarPropertyValue<T> implements Parcelable {
39     private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
40 
41     private final int mPropertyId;
42     private final int mAreaId;
43     private final int mStatus;
44     private final long mTimestamp;
45     private final T mValue;
46 
47     @IntDef({
48         STATUS_AVAILABLE,
49         STATUS_UNAVAILABLE,
50         STATUS_ERROR
51     })
52     @Retention(RetentionPolicy.SOURCE)
53     public @interface PropertyStatus {}
54 
55     /**
56      * CarPropertyValue is available.
57      */
58     public static final int STATUS_AVAILABLE = 0;
59 
60     /**
61      * CarPropertyValue is unavailable.
62      */
63     public static final int STATUS_UNAVAILABLE = 1;
64 
65     /**
66      * CarPropertyVale has an error.
67      */
68     public static final int STATUS_ERROR = 2;
69 
70     /**
71      * Get an instance of CarPropertyValue
72      * @param propertyId Property ID
73      * @param areaId Area ID of Property
74      * @param value Value of Property
75      * @hide
76      */
CarPropertyValue(int propertyId, int areaId, T value)77     public CarPropertyValue(int propertyId, int areaId, T value) {
78         this(propertyId, areaId, 0, 0, value);
79     }
80 
81     /**
82      * Get an instance of CarPropertyValue
83      * @param propertyId Property ID
84      * @param areaId Area ID of Property
85      * @param status Status of Property
86      * @param timestamp Timestamp in nanosecond
87      * @param value Value of Property
88      * @hide
89      */
CarPropertyValue(int propertyId, int areaId, int status, long timestamp, T value)90     public CarPropertyValue(int propertyId, int areaId, int status, long timestamp, T value) {
91         mPropertyId = propertyId;
92         mAreaId = areaId;
93         mStatus = status;
94         mTimestamp = timestamp;
95         mValue = value;
96     }
97 
98     /**
99      * Get an instance of CarPropertyValue
100      * @param in Parcel to read
101      * @hide
102      */
103     @SuppressWarnings("unchecked")
CarPropertyValue(Parcel in)104     public CarPropertyValue(Parcel in) {
105         mPropertyId = in.readInt();
106         mAreaId = in.readInt();
107         mStatus = in.readInt();
108         mTimestamp = in.readLong();
109         String valueClassName = in.readString();
110         Class<?> valueClass;
111         try {
112             valueClass = Class.forName(valueClassName);
113         } catch (ClassNotFoundException e) {
114             throw new IllegalArgumentException("Class not found: " + valueClassName);
115         }
116 
117         if (String.class.equals(valueClass)) {
118             byte[] bytes = in.readBlob();
119             mValue = (T) new String(bytes, DEFAULT_CHARSET);
120         } else if (byte[].class.equals(valueClass)) {
121             mValue = (T) in.readBlob();
122         } else {
123             mValue = (T) in.readValue(valueClass.getClassLoader());
124         }
125     }
126 
127     public static final Creator<CarPropertyValue> CREATOR = new Creator<CarPropertyValue>() {
128         @Override
129         public CarPropertyValue createFromParcel(Parcel in) {
130             return new CarPropertyValue(in);
131         }
132 
133         @Override
134         public CarPropertyValue[] newArray(int size) {
135             return new CarPropertyValue[size];
136         }
137     };
138 
139     @Override
describeContents()140     public int describeContents() {
141         return 0;
142     }
143 
144     @Override
writeToParcel(Parcel dest, int flags)145     public void writeToParcel(Parcel dest, int flags) {
146         dest.writeInt(mPropertyId);
147         dest.writeInt(mAreaId);
148         dest.writeInt(mStatus);
149         dest.writeLong(mTimestamp);
150 
151         Class<?> valueClass = mValue == null ? null : mValue.getClass();
152         dest.writeString(valueClass == null ? null : valueClass.getName());
153 
154         // Special handling for String and byte[] to mitigate transaction buffer limitations.
155         if (String.class.equals(valueClass)) {
156             dest.writeBlob(((String) mValue).getBytes(DEFAULT_CHARSET));
157         } else if (byte[].class.equals(valueClass)) {
158             dest.writeBlob((byte[]) mValue);
159         } else {
160             dest.writeValue(mValue);
161         }
162     }
163 
164     /**
165      * @return Property id of CarPropertyValue
166      */
getPropertyId()167     public int getPropertyId() {
168         return mPropertyId;
169     }
170 
171     /**
172      * @return Area id of CarPropertyValue
173      */
getAreaId()174     public int getAreaId() {
175         return mAreaId;
176     }
177 
178     /**
179      * @return Status of CarPropertyValue
180      */
getStatus()181     public @PropertyStatus int getStatus() {
182         return mStatus;
183     }
184 
185     /**
186      * @return Timestamp of CarPropertyValue
187      */
getTimestamp()188     public long getTimestamp() {
189         return mTimestamp;
190     }
191 
192     /**
193      * @return Value of CarPropertyValue
194      */
195     @NonNull
getValue()196     public T getValue() {
197         return mValue;
198     }
199 
200     /** @hide */
201     @Override
toString()202     public String toString() {
203         return "CarPropertyValue{"
204                 + "mPropertyId=0x" + toHexString(mPropertyId)
205                 + ", mAreaId=0x" + toHexString(mAreaId)
206                 + ", mStatus=" + mStatus
207                 + ", mTimestamp=" + mTimestamp
208                 + ", mValue=" + mValue
209                 + '}';
210     }
211 }
212