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