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 * <p>This class is a java representation of {@code struct VehiclePropValue} defined in 35 * {@code hardware/interfaces/automotive/vehicle/2.0/types.hal}. See 36 * {@link com.android.car.hal.CarPropertyUtils} to learn conversion details. 37 * 38 * @param <T> refer to Parcel#writeValue(Object) to get a list of all supported types. The class 39 * should be visible to framework as default class loader is being used here. 40 */ 41 public final class CarPropertyValue<T> implements Parcelable { 42 private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; 43 44 private final int mPropertyId; 45 private final int mAreaId; 46 private final int mStatus; 47 private final long mTimestamp; 48 private final T mValue; 49 50 @IntDef({ 51 STATUS_AVAILABLE, 52 STATUS_UNAVAILABLE, 53 STATUS_ERROR 54 }) 55 @Retention(RetentionPolicy.SOURCE) 56 public @interface PropertyStatus {} 57 58 /** 59 * CarPropertyValue is available. 60 */ 61 public static final int STATUS_AVAILABLE = 0; 62 63 /** 64 * CarPropertyValue is unavailable. 65 */ 66 public static final int STATUS_UNAVAILABLE = 1; 67 68 /** 69 * CarPropertyVale has an error. 70 */ 71 public static final int STATUS_ERROR = 2; 72 73 /** 74 * Creates an instance of CarPropertyValue. 75 * 76 * @param propertyId Property ID 77 * @param areaId Area ID of Property 78 * @param value Value of Property 79 * @hide 80 */ CarPropertyValue(int propertyId, int areaId, T value)81 public CarPropertyValue(int propertyId, int areaId, T value) { 82 this(propertyId, areaId, 0, 0, value); 83 } 84 85 /** 86 * Creates an instance of CarPropertyValue. The {@code timestamp} is the time in nanoseconds at 87 * which the event happened. For a given car property, each new CarPropertyValue should be 88 * monotonically increasing using the same time base as 89 * {@link SystemClock#elapsedRealtimeNanos()}. 90 * 91 * @param propertyId Property ID 92 * @param areaId Area ID of Property 93 * @param status Status of Property 94 * @param timestamp Elapsed time in nanoseconds since boot 95 * @param value Value of Property 96 * @hide 97 */ CarPropertyValue(int propertyId, int areaId, int status, long timestamp, T value)98 public CarPropertyValue(int propertyId, int areaId, int status, long timestamp, T value) { 99 mPropertyId = propertyId; 100 mAreaId = areaId; 101 mStatus = status; 102 mTimestamp = timestamp; 103 mValue = value; 104 } 105 106 /** 107 * Creates an instance of CarPropertyValue. 108 * 109 * @param in Parcel to read 110 * @hide 111 */ 112 @SuppressWarnings("unchecked") CarPropertyValue(Parcel in)113 public CarPropertyValue(Parcel in) { 114 mPropertyId = in.readInt(); 115 mAreaId = in.readInt(); 116 mStatus = in.readInt(); 117 mTimestamp = in.readLong(); 118 String valueClassName = in.readString(); 119 Class<?> valueClass; 120 try { 121 valueClass = Class.forName(valueClassName); 122 } catch (ClassNotFoundException e) { 123 throw new IllegalArgumentException("Class not found: " + valueClassName); 124 } 125 126 if (String.class.equals(valueClass)) { 127 byte[] bytes = in.readBlob(); 128 mValue = (T) new String(bytes, DEFAULT_CHARSET); 129 } else if (byte[].class.equals(valueClass)) { 130 mValue = (T) in.readBlob(); 131 } else { 132 mValue = (T) in.readValue(valueClass.getClassLoader()); 133 } 134 } 135 136 public static final Creator<CarPropertyValue> CREATOR = new Creator<CarPropertyValue>() { 137 @Override 138 public CarPropertyValue createFromParcel(Parcel in) { 139 return new CarPropertyValue(in); 140 } 141 142 @Override 143 public CarPropertyValue[] newArray(int size) { 144 return new CarPropertyValue[size]; 145 } 146 }; 147 148 @Override describeContents()149 public int describeContents() { 150 return 0; 151 } 152 153 @Override writeToParcel(Parcel dest, int flags)154 public void writeToParcel(Parcel dest, int flags) { 155 dest.writeInt(mPropertyId); 156 dest.writeInt(mAreaId); 157 dest.writeInt(mStatus); 158 dest.writeLong(mTimestamp); 159 160 Class<?> valueClass = mValue == null ? null : mValue.getClass(); 161 dest.writeString(valueClass == null ? null : valueClass.getName()); 162 163 // Special handling for String and byte[] to mitigate transaction buffer limitations. 164 if (String.class.equals(valueClass)) { 165 dest.writeBlob(((String) mValue).getBytes(DEFAULT_CHARSET)); 166 } else if (byte[].class.equals(valueClass)) { 167 dest.writeBlob((byte[]) mValue); 168 } else { 169 dest.writeValue(mValue); 170 } 171 } 172 173 /** 174 * @return Property id of CarPropertyValue 175 */ getPropertyId()176 public int getPropertyId() { 177 return mPropertyId; 178 } 179 180 /** 181 * @return Area id of CarPropertyValue 182 */ getAreaId()183 public int getAreaId() { 184 return mAreaId; 185 } 186 187 /** 188 * @return Status of CarPropertyValue 189 */ getStatus()190 public @PropertyStatus int getStatus() { 191 return mStatus; 192 } 193 194 /** 195 * Returns the timestamp in nanoseconds at which the CarPropertyValue happened. For a given car 196 * property, each new CarPropertyValue should be monotonically increasing using the same time 197 * base as {@link SystemClock#elapsedRealtimeNanos()}. 198 * 199 * <p>NOTE: Timestamp should be synchronized with other signals from the platform (e.g. 200 * {@link Location} and {@link SensorEvent} instances). Ideally, timestamp synchronization 201 * error should be below 1 millisecond. 202 */ getTimestamp()203 public long getTimestamp() { 204 return mTimestamp; 205 } 206 207 /** 208 * @return Value of CarPropertyValue 209 */ 210 @NonNull getValue()211 public T getValue() { 212 return mValue; 213 } 214 215 /** @hide */ 216 @Override toString()217 public String toString() { 218 return "CarPropertyValue{" 219 + "mPropertyId=0x" + toHexString(mPropertyId) 220 + ", mAreaId=0x" + toHexString(mAreaId) 221 + ", mStatus=" + mStatus 222 + ", mTimestamp=" + mTimestamp 223 + ", mValue=" + mValue 224 + '}'; 225 } 226 } 227