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 com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 20 21 import static java.lang.Integer.toHexString; 22 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.car.VehiclePropertyIds; 26 import android.car.annotation.AddedInOrBefore; 27 import android.car.builtin.os.ParcelHelper; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 31 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.nio.charset.Charset; 36 import java.nio.charset.StandardCharsets; 37 import java.util.Objects; 38 39 /** 40 * Stores a value for a vehicle property ID and area ID combination. 41 * 42 * Client should use {@code android.car.*} types when dealing with property ID, area ID or property 43 * value and MUST NOT use {@code android.hardware.automotive.vehicle.*} types directly. 44 * 45 * @param <T> refer to {@link Parcel#writeValue(java.lang.Object)} to get a list of all supported 46 * types. The class should be visible to framework as default class loader is being used 47 * here. 48 */ 49 public final class CarPropertyValue<T> implements Parcelable { 50 private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; 51 52 private final int mPropertyId; 53 private final int mAreaId; 54 private final int mStatus; 55 private final long mTimestampNanos; 56 private final T mValue; 57 58 @IntDef({ 59 STATUS_AVAILABLE, 60 STATUS_UNAVAILABLE, 61 STATUS_ERROR 62 }) 63 @Retention(RetentionPolicy.SOURCE) 64 public @interface PropertyStatus {} 65 66 /** 67 * {@code CarPropertyValue} is available. 68 */ 69 @AddedInOrBefore(majorVersion = 33) 70 public static final int STATUS_AVAILABLE = 0; 71 72 /** 73 * {@code CarPropertyValue} is unavailable. 74 */ 75 @AddedInOrBefore(majorVersion = 33) 76 public static final int STATUS_UNAVAILABLE = 1; 77 78 /** 79 * {@code CarPropertyValue} has an error. 80 */ 81 @AddedInOrBefore(majorVersion = 33) 82 public static final int STATUS_ERROR = 2; 83 84 /** 85 * Creates an instance of {@code CarPropertyValue}. 86 * 87 * @param propertyId Property ID, must be one of enums in 88 * {@link android.car.VehiclePropertyIds}. 89 * @param areaId Area ID of Property, must be one of enums in one of the following classes: 90 * <ul> 91 * <li><{@code VehicleAreaWindow}</li> 92 * <li><{@code VehicleAreaDoor}</li> 93 * <li><{@link android.car.VehicleAreaSeat}</li> 94 * <li><{@code VehicleAreaMirror}</li> 95 * <li><{@link android.car.VehicleAreaWheel}</li> 96 * </ul> 97 * or 0 for global property. 98 * @param value Value of Property 99 * @hide 100 */ CarPropertyValue(int propertyId, int areaId, T value)101 public CarPropertyValue(int propertyId, int areaId, T value) { 102 this(propertyId, areaId, /* timestampNanos= */ 0, value); 103 } 104 105 /** 106 * Creates an instance of {@code CarPropertyValue}. The {@code timestampNanos} is the time in 107 * nanoseconds at which the event happened. For a given car property, each new {@code 108 * CarPropertyValue} should be monotonically increasing using the same time base as 109 * {@link SystemClock#elapsedRealtimeNanos()}. 110 * 111 * @param propertyId Property ID, must be one of enums in 112 * {@link android.car.VehiclePropertyIds}. 113 * @param areaId Area ID of Property, must be one of enums in one of the following classes: 114 * <ul> 115 * <li><{@code VehicleAreaWindow}</li> 116 * <li><{@code VehicleAreaDoor}</li> 117 * <li><{@link android.car.VehicleAreaSeat}</li> 118 * <li><{@code VehicleAreaMirror}</li> 119 * <li><{@link android.car.VehicleAreaWheel}</li> 120 * </ul> 121 * or 0 for global property. 122 * @param timestampNanos Elapsed time in nanoseconds since boot 123 * @param value Value of Property 124 * @hide 125 */ CarPropertyValue(int propertyId, int areaId, long timestampNanos, T value)126 public CarPropertyValue(int propertyId, int areaId, long timestampNanos, T value) { 127 this(propertyId, areaId, CarPropertyValue.STATUS_AVAILABLE, timestampNanos, value); 128 } 129 130 /** 131 * @hide 132 * 133 * @deprecated use {@link CarPropertyValue#CarPropertyValue(int, int, long, T)} instead 134 */ 135 @Deprecated CarPropertyValue(int propertyId, int areaId, int status, long timestampNanos, T value)136 public CarPropertyValue(int propertyId, int areaId, int status, long timestampNanos, T value) { 137 mPropertyId = propertyId; 138 mAreaId = areaId; 139 mStatus = status; 140 mTimestampNanos = timestampNanos; 141 mValue = value; 142 } 143 144 /** 145 * Creates an instance of {@code CarPropertyValue}. 146 * 147 * @param in Parcel to read 148 * @hide 149 */ 150 @SuppressWarnings("unchecked") CarPropertyValue(Parcel in)151 public CarPropertyValue(Parcel in) { 152 mPropertyId = in.readInt(); 153 mAreaId = in.readInt(); 154 mStatus = in.readInt(); 155 mTimestampNanos = in.readLong(); 156 String valueClassName = in.readString(); 157 Class<?> valueClass; 158 try { 159 valueClass = Class.forName(valueClassName); 160 } catch (ClassNotFoundException e) { 161 throw new IllegalArgumentException("Class not found: " + valueClassName, e); 162 } 163 164 if (String.class.equals(valueClass)) { 165 byte[] bytes = ParcelHelper.readBlob(in); 166 mValue = (T) new String(bytes, DEFAULT_CHARSET); 167 } else if (byte[].class.equals(valueClass)) { 168 mValue = (T) ParcelHelper.readBlob(in); 169 } else { 170 mValue = (T) in.readValue(valueClass.getClassLoader()); 171 } 172 } 173 174 @AddedInOrBefore(majorVersion = 33) 175 public static final Creator<CarPropertyValue> CREATOR = new Creator<CarPropertyValue>() { 176 @Override 177 public CarPropertyValue createFromParcel(Parcel in) { 178 return new CarPropertyValue(in); 179 } 180 181 @Override 182 public CarPropertyValue[] newArray(int size) { 183 return new CarPropertyValue[size]; 184 } 185 }; 186 187 @Override 188 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 189 @AddedInOrBefore(majorVersion = 33) describeContents()190 public int describeContents() { 191 return 0; 192 } 193 194 @Override 195 @AddedInOrBefore(majorVersion = 33) writeToParcel(Parcel dest, int flags)196 public void writeToParcel(Parcel dest, int flags) { 197 dest.writeInt(mPropertyId); 198 dest.writeInt(mAreaId); 199 dest.writeInt(mStatus); 200 dest.writeLong(mTimestampNanos); 201 202 Class<?> valueClass = mValue == null ? null : mValue.getClass(); 203 dest.writeString(valueClass == null ? null : valueClass.getName()); 204 205 // Special handling for String and byte[] to mitigate transaction buffer limitations. 206 if (String.class.equals(valueClass)) { 207 ParcelHelper.writeBlob(dest, ((String) mValue).getBytes(DEFAULT_CHARSET)); 208 } else if (byte[].class.equals(valueClass)) { 209 ParcelHelper.writeBlob(dest, (byte[]) mValue); 210 } else { 211 dest.writeValue(mValue); 212 } 213 } 214 215 /** 216 * @return Property id of {@code CarPropertyValue}, must be one of enums in 217 * {@link android.car.VehiclePropertyIds}. 218 */ 219 @AddedInOrBefore(majorVersion = 33) getPropertyId()220 public int getPropertyId() { 221 return mPropertyId; 222 } 223 224 /** 225 * @return Area id of {@code CarPropertyValue}, must be one of enums in one of the following 226 * classes: 227 * <ul> 228 * <li><{@code VehicleAreaWindow}</li> 229 * <li><{@code VehicleAreaDoor}</li> 230 * <li><{@link android.car.VehicleAreaSeat}</li> 231 * <li><{@code VehicleAreaMirror}</li> 232 * <li><{@link android.car.VehicleAreaWheel}</li> 233 * </ul> 234 * or 0 for global property. 235 */ 236 @AddedInOrBefore(majorVersion = 33) getAreaId()237 public int getAreaId() { 238 return mAreaId; 239 } 240 241 /** 242 * @return Status of {@code CarPropertyValue} 243 * 244 * @deprecated This should be added back in next major Android release. 245 */ 246 @AddedInOrBefore(majorVersion = 33) getStatus()247 public @PropertyStatus int getStatus() { 248 return mStatus; 249 } 250 251 /** 252 * Returns the timestamp in nanoseconds at which the {@code CarPropertyValue} happened. For a 253 * given car property, each new {@code CarPropertyValue} should be monotonically increasing 254 * using the same time base as {@link android.os.SystemClock#elapsedRealtimeNanos()}. 255 * 256 * <p>NOTE: Timestamp should be synchronized with other signals from the platform (e.g. 257 * {@link android.location.Location} and {@link android.hardware.SensorEvent} instances). 258 * Ideally, timestamp synchronization error should be below 1 millisecond. 259 */ 260 @AddedInOrBefore(majorVersion = 33) getTimestamp()261 public long getTimestamp() { 262 return mTimestampNanos; 263 } 264 265 /** 266 * Returns the value for {@code CarPropertyValue}. 267 * 268 * <p> 269 * <b>Note:</b>Caller must check the value of {@link #getStatus()}. Only use 270 * {@link #getValue()} when {@link #getStatus()} is {@link #STATUS_AVAILABLE}. If not, 271 * {@link #getValue()} is meaningless. 272 */ 273 @NonNull 274 @AddedInOrBefore(majorVersion = 33) getValue()275 public T getValue() { 276 return mValue; 277 } 278 279 /** @hide */ 280 @Override toString()281 public String toString() { 282 return "CarPropertyValue{" 283 + "mPropertyId=0x" + toHexString(mPropertyId) 284 + ", propertyName=" + VehiclePropertyIds.toString(mPropertyId) 285 + ", mAreaId=0x" + toHexString(mAreaId) 286 + ", mStatus=" + mStatus 287 + ", mTimestampNanos=" + mTimestampNanos 288 + ", mValue=" + mValue 289 + '}'; 290 } 291 292 /** Generates hash code for this instance. */ 293 @Override hashCode()294 public int hashCode() { 295 return Objects.hash(mPropertyId, mAreaId, mStatus, mTimestampNanos, mValue); 296 } 297 298 /** Checks equality with passed {@code object}. */ 299 @Override equals(Object object)300 public boolean equals(Object object) { 301 if (this == object) { 302 return true; 303 } 304 if (!(object instanceof CarPropertyValue<?>)) { 305 return false; 306 } 307 CarPropertyValue<?> carPropertyValue = (CarPropertyValue<?>) object; 308 return mPropertyId == carPropertyValue.mPropertyId && mAreaId == carPropertyValue.mAreaId 309 && mStatus == carPropertyValue.mStatus 310 && mTimestampNanos == carPropertyValue.mTimestampNanos 311 && Objects.equals(mValue, carPropertyValue.mValue); 312 } 313 } 314