/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.car.hardware; import static java.lang.Integer.toHexString; import android.annotation.IntDef; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** * Stores values broken down by area for a vehicle property. * *

This class is a java representation of {@code struct VehiclePropValue} defined in * {@code hardware/interfaces/automotive/vehicle/2.0/types.hal}. See * {@link com.android.car.hal.CarPropertyUtils} to learn conversion details. * * @param refer to Parcel#writeValue(Object) to get a list of all supported types. The class * should be visible to framework as default class loader is being used here. */ public final class CarPropertyValue implements Parcelable { private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; private final int mPropertyId; private final int mAreaId; private final int mStatus; private final long mTimestamp; private final T mValue; @IntDef({ STATUS_AVAILABLE, STATUS_UNAVAILABLE, STATUS_ERROR }) @Retention(RetentionPolicy.SOURCE) public @interface PropertyStatus {} /** * CarPropertyValue is available. */ public static final int STATUS_AVAILABLE = 0; /** * CarPropertyValue is unavailable. */ public static final int STATUS_UNAVAILABLE = 1; /** * CarPropertyVale has an error. */ public static final int STATUS_ERROR = 2; /** * Creates an instance of CarPropertyValue. * * @param propertyId Property ID * @param areaId Area ID of Property * @param value Value of Property * @hide */ public CarPropertyValue(int propertyId, int areaId, T value) { this(propertyId, areaId, 0, 0, value); } /** * Creates an instance of CarPropertyValue. The {@code timestamp} is the time in nanoseconds at * which the event happened. For a given car property, each new CarPropertyValue should be * monotonically increasing using the same time base as * {@link SystemClock#elapsedRealtimeNanos()}. * * @param propertyId Property ID * @param areaId Area ID of Property * @param status Status of Property * @param timestamp Elapsed time in nanoseconds since boot * @param value Value of Property * @hide */ public CarPropertyValue(int propertyId, int areaId, int status, long timestamp, T value) { mPropertyId = propertyId; mAreaId = areaId; mStatus = status; mTimestamp = timestamp; mValue = value; } /** * Creates an instance of CarPropertyValue. * * @param in Parcel to read * @hide */ @SuppressWarnings("unchecked") public CarPropertyValue(Parcel in) { mPropertyId = in.readInt(); mAreaId = in.readInt(); mStatus = in.readInt(); mTimestamp = in.readLong(); String valueClassName = in.readString(); Class valueClass; try { valueClass = Class.forName(valueClassName); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Class not found: " + valueClassName); } if (String.class.equals(valueClass)) { byte[] bytes = in.readBlob(); mValue = (T) new String(bytes, DEFAULT_CHARSET); } else if (byte[].class.equals(valueClass)) { mValue = (T) in.readBlob(); } else { mValue = (T) in.readValue(valueClass.getClassLoader()); } } public static final Creator CREATOR = new Creator() { @Override public CarPropertyValue createFromParcel(Parcel in) { return new CarPropertyValue(in); } @Override public CarPropertyValue[] newArray(int size) { return new CarPropertyValue[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mPropertyId); dest.writeInt(mAreaId); dest.writeInt(mStatus); dest.writeLong(mTimestamp); Class valueClass = mValue == null ? null : mValue.getClass(); dest.writeString(valueClass == null ? null : valueClass.getName()); // Special handling for String and byte[] to mitigate transaction buffer limitations. if (String.class.equals(valueClass)) { dest.writeBlob(((String) mValue).getBytes(DEFAULT_CHARSET)); } else if (byte[].class.equals(valueClass)) { dest.writeBlob((byte[]) mValue); } else { dest.writeValue(mValue); } } /** * @return Property id of CarPropertyValue */ public int getPropertyId() { return mPropertyId; } /** * @return Area id of CarPropertyValue */ public int getAreaId() { return mAreaId; } /** * @return Status of CarPropertyValue */ public @PropertyStatus int getStatus() { return mStatus; } /** * Returns the timestamp in nanoseconds at which the CarPropertyValue happened. For a given car * property, each new CarPropertyValue should be monotonically increasing using the same time * base as {@link SystemClock#elapsedRealtimeNanos()}. * *

NOTE: Timestamp should be synchronized with other signals from the platform (e.g. * {@link Location} and {@link SensorEvent} instances). Ideally, timestamp synchronization * error should be below 1 millisecond. */ public long getTimestamp() { return mTimestamp; } /** * @return Value of CarPropertyValue */ @NonNull public T getValue() { return mValue; } /** @hide */ @Override public String toString() { return "CarPropertyValue{" + "mPropertyId=0x" + toHexString(mPropertyId) + ", mAreaId=0x" + toHexString(mAreaId) + ", mStatus=" + mStatus + ", mTimestamp=" + mTimestamp + ", mValue=" + mValue + '}'; } }