Register/Unregister implementation of AtomotiveCarClimate.
The CL only covers a basic successful response unit test
for AutomotiveCarClimate APIs. Thorough testing will be performed
in follow-up CLs.
Relnote: Adding register/unregister APIs for AutomotiveCarClimate.
Bug: 215225317
Test: ./gradlew :car:app:app-automotive:build
Change-Id: If2dd39d94a58566240d761d75a9265f82cb3e430
diff --git a/car/app/app-automotive/build.gradle b/car/app/app-automotive/build.gradle
index 4314d26..27a74b0 100644
--- a/car/app/app-automotive/build.gradle
+++ b/car/app/app-automotive/build.gradle
@@ -34,6 +34,7 @@
// There is an implicit compile-only dep due to :annotation-experimental
// Build will complain without this manual declaration.
api(libs.kotlinStdlib)
+ implementation project(path: ':annotation:annotation-experimental')
annotationProcessor(libs.nullaway)
annotationProcessor(libs.autoValue)
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/AutomotiveCarHardwareManager.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/AutomotiveCarHardwareManager.java
index acd7164..445bfbb 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/AutomotiveCarHardwareManager.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/AutomotiveCarHardwareManager.java
@@ -47,9 +47,10 @@
public AutomotiveCarHardwareManager(@NonNull Context context) {
Context appContext = requireNonNull(context.getApplicationContext());
- mCarInfo = new AutomotiveCarInfo(new PropertyManager(appContext));
+ PropertyManager mPropertyManager = new PropertyManager(appContext);
+ mCarInfo = new AutomotiveCarInfo(mPropertyManager);
mCarSensors = new AutomotiveCarSensors();
- mCarClimate = new AutomotiveCarClimate();
+ mCarClimate = new AutomotiveCarClimate(mPropertyManager);
}
/** @hide */
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/climate/AutomotiveCarClimate.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/climate/AutomotiveCarClimate.java
index 68caf91..6af4dfd 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/climate/AutomotiveCarClimate.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/climate/AutomotiveCarClimate.java
@@ -16,13 +16,61 @@
package androidx.car.app.hardware.climate;
+import static android.car.VehiclePropertyIds.HVAC_AC_ON;
+import static android.car.VehiclePropertyIds.HVAC_AUTO_ON;
+import static android.car.VehiclePropertyIds.HVAC_AUTO_RECIRC_ON;
+import static android.car.VehiclePropertyIds.HVAC_DEFROSTER;
+import static android.car.VehiclePropertyIds.HVAC_DUAL_ON;
+import static android.car.VehiclePropertyIds.HVAC_FAN_DIRECTION;
+import static android.car.VehiclePropertyIds.HVAC_FAN_SPEED;
+import static android.car.VehiclePropertyIds.HVAC_MAX_AC_ON;
+import static android.car.VehiclePropertyIds.HVAC_MAX_DEFROST_ON;
+import static android.car.VehiclePropertyIds.HVAC_POWER_ON;
+import static android.car.VehiclePropertyIds.HVAC_RECIRC_ON;
+import static android.car.VehiclePropertyIds.HVAC_SEAT_TEMPERATURE;
+import static android.car.VehiclePropertyIds.HVAC_SEAT_VENTILATION;
+import static android.car.VehiclePropertyIds.HVAC_STEERING_WHEEL_HEAT;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
+
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_CABIN_TEMPERATURE;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_FAN_DIRECTION;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_FAN_SPEED;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_AC;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_AUTO_MODE;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_AUTO_RECIRCULATION;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_DEFROSTER;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_DUAL_MODE;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_MAX_AC;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_MAX_DEFROSTER;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_POWER;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_RECIRCULATION;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_SEAT_TEMPERATURE_LEVEL;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_SEAT_VENTILATION_LEVEL;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_STEERING_WHEEL_HEAT;
+import static androidx.car.app.hardware.common.CarValueUtils.getCarValue;
+
+import static java.util.Objects.requireNonNull;
+
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.car.app.annotations.ExperimentalCarApi;
+import androidx.car.app.hardware.common.CarPropertyResponse;
import androidx.car.app.hardware.common.CarSetOperationStatusCallback;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.CarZone;
+import androidx.car.app.hardware.common.OnCarPropertyResponseListener;
+import androidx.car.app.hardware.common.PropertyManager;
+import androidx.car.app.utils.LogTags;
+import com.google.common.collect.ImmutableBiMap;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -34,19 +82,60 @@
@ExperimentalCarApi
public class AutomotiveCarClimate implements CarClimate {
- public AutomotiveCarClimate() {
+ @VisibleForTesting
+ static final float DEFAULT_SAMPLE_RATE_HZ = 5f;
+ static ImmutableBiMap<Integer, Integer> sFeatureToPropertyId =
+ new ImmutableBiMap.Builder<Integer,
+ Integer>()
+ .put(FEATURE_HVAC_POWER, HVAC_POWER_ON)
+ .put(FEATURE_HVAC_AC, HVAC_AC_ON)
+ .put(FEATURE_HVAC_MAX_AC, HVAC_MAX_AC_ON)
+ .put(FEATURE_CABIN_TEMPERATURE, HVAC_TEMPERATURE_SET)
+ .put(FEATURE_FAN_SPEED, HVAC_FAN_SPEED)
+ .put(FEATURE_FAN_DIRECTION, HVAC_FAN_DIRECTION)
+ .put(FEATURE_SEAT_TEMPERATURE_LEVEL, HVAC_SEAT_TEMPERATURE)
+ .put(FEATURE_SEAT_VENTILATION_LEVEL, HVAC_SEAT_VENTILATION)
+ .put(FEATURE_STEERING_WHEEL_HEAT, HVAC_STEERING_WHEEL_HEAT)
+ .put(FEATURE_HVAC_RECIRCULATION, HVAC_RECIRC_ON)
+ .put(FEATURE_HVAC_AUTO_RECIRCULATION, HVAC_AUTO_RECIRC_ON)
+ .put(FEATURE_HVAC_AUTO_MODE, HVAC_AUTO_ON)
+ .put(FEATURE_HVAC_DUAL_MODE, HVAC_DUAL_ON)
+ .put(FEATURE_HVAC_DEFROSTER, HVAC_DEFROSTER)
+ .put(FEATURE_HVAC_MAX_DEFROSTER, HVAC_MAX_DEFROST_ON)
+ .buildOrThrow();
+
+ private final Map<CarClimateStateCallback, OnCarPropertyResponseListener> mListenerMap =
+ new HashMap<>();
+
+ private final PropertyManager mPropertyManager;
+
+ public AutomotiveCarClimate(@NonNull PropertyManager manager) {
+ mPropertyManager = requireNonNull(manager);
}
@Override
public void registerClimateStateCallback(@NonNull Executor executor,
@NonNull RegisterClimateStateRequest request,
@NonNull CarClimateStateCallback callback) {
-
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones = new HashMap<>();
+ for (CarClimateFeature feature : request.getClimateRegisterFeatures()) {
+ int propertyId = requireNonNull(sFeatureToPropertyId.get(feature.getFeature()));
+ propertyIdsWithCarZones.put(propertyId, feature.getCarZones());
+ }
+ PropertyListener listener = new PropertyListener(callback, executor);
+ mPropertyManager.submitRegisterListenerRequest(propertyIdsWithCarZones,
+ DEFAULT_SAMPLE_RATE_HZ,
+ listener,
+ executor);
+ mListenerMap.put(callback, listener);
}
@Override
public void unregisterClimateStateCallback(@NonNull CarClimateStateCallback callback) {
-
+ OnCarPropertyResponseListener responseListener = mListenerMap.remove(callback);
+ if (responseListener != null) {
+ mPropertyManager.submitUnregisterListenerRequest(responseListener);
+ }
}
@Override
@@ -62,4 +151,98 @@
@NonNull CarSetOperationStatusCallback callback) {
}
+
+ private static class PropertyListener implements OnCarPropertyResponseListener {
+ private final Executor mExecutor;
+ private final CarClimateStateCallback mCarClimateStateCallback;
+
+ PropertyListener(CarClimateStateCallback callback, Executor executor) {
+ mCarClimateStateCallback = callback;
+ mExecutor = executor;
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked", "unsafe"})
+ public void onCarPropertyResponses(
+ @NonNull List<CarPropertyResponse<?>> carPropertyResponses) {
+ mExecutor.execute(() -> {
+ for (CarPropertyResponse<?> response : carPropertyResponses) {
+ Integer mFeature = sFeatureToPropertyId.inverse().get(response.getPropertyId());
+ if (mFeature == null) {
+ Log.e(LogTags.TAG_CAR_HARDWARE, "Feature not found for property Id "
+ + response.getPropertyId());
+ continue;
+ }
+ CarValue<?> mCarValue = getCarValue(response, response.getValue());
+ switch (mFeature) {
+ case FEATURE_HVAC_POWER:
+ mCarClimateStateCallback.onHvacPowerStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_HVAC_AC:
+ mCarClimateStateCallback.onHvacAcStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_HVAC_MAX_AC:
+ mCarClimateStateCallback.onHvacMaxAcModeStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_CABIN_TEMPERATURE:
+ mCarClimateStateCallback.onCabinTemperatureStateAvailable(
+ (CarValue<Float>) mCarValue);
+ break;
+ case FEATURE_FAN_SPEED:
+ mCarClimateStateCallback.onFanSpeedLevelStateAvailable(
+ (CarValue<Integer>) mCarValue);
+ break;
+ case FEATURE_FAN_DIRECTION:
+ mCarClimateStateCallback.onFanDirectionStateAvailable(
+ (CarValue<Integer>) mCarValue);
+ break;
+ case FEATURE_SEAT_TEMPERATURE_LEVEL:
+ mCarClimateStateCallback.onSeatTemperatureLevelStateAvailable(
+ (CarValue<Integer>) mCarValue);
+ break;
+ case FEATURE_SEAT_VENTILATION_LEVEL:
+ mCarClimateStateCallback.onSeatVentilationLevelStateAvailable(
+ (CarValue<Integer>) mCarValue);
+ break;
+ case FEATURE_STEERING_WHEEL_HEAT:
+ mCarClimateStateCallback.onSteeringWheelHeatStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_HVAC_RECIRCULATION:
+ mCarClimateStateCallback.onHvacRecirculationStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_HVAC_AUTO_RECIRCULATION:
+ mCarClimateStateCallback.onHvacAutoRecirculationStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_HVAC_AUTO_MODE:
+ mCarClimateStateCallback.onHvacAutoModeStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_HVAC_DUAL_MODE:
+ mCarClimateStateCallback.onHvacDualModeStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_HVAC_DEFROSTER:
+ mCarClimateStateCallback.onDefrosterStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ case FEATURE_HVAC_MAX_DEFROSTER:
+ mCarClimateStateCallback.onMaxDefrosterStateAvailable(
+ (CarValue<Boolean>) mCarValue);
+ break;
+ default:
+ Log.e(LogTags.TAG_CAR_HARDWARE,
+ "Invalid response callback in PropertyListener with "
+ + "feature value: " + mFeature);
+ break;
+ }
+ }
+ });
+ }
+ }
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/CarValueUtils.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/CarValueUtils.java
new file mode 100644
index 0000000..381c855
--- /dev/null
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/CarValueUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 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 androidx.car.app.hardware.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.ExperimentalCarApi;
+
+import java.util.List;
+
+/**
+ * Utility functions to work with {@link androidx.car.app.hardware.info.AutomotiveCarInfo} and
+ * {@link androidx.car.app.hardware.climate.AutomotiveCarClimate}
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+public final class CarValueUtils {
+ /**
+ * Gets a {@link androidx.car.app.hardware.common.CarValue} object from
+ * {@link androidx.car.app.hardware.common.CarPropertyResponse}
+ */
+ @NonNull
+ @OptIn(markerClass = ExperimentalCarApi.class)
+ public static <T> CarValue<T> getCarValue(@NonNull CarPropertyResponse<?> response,
+ @Nullable T value) {
+ long timestampMillis = response.getTimestampMillis();
+ int status = response.getStatus();
+ List<CarZone> zones = response.getCarZones();
+ return new CarValue<>(value, timestampMillis, status, zones);
+ }
+
+ private CarValueUtils() {
+ }
+}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyIdAreaId.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyIdAreaId.java
new file mode 100644
index 0000000..93d568b
--- /dev/null
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyIdAreaId.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 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 androidx.car.app.hardware.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.ExperimentalCarApi;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * Container class for information about property Ids and area Ids.
+ *
+ * <p>The hash code generated by the auto class is used as uId.
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+@AutoValue
+public abstract class PropertyIdAreaId {
+
+ /** Returns one of the property Ids in {@link android.car.VehiclePropertyIds}. */
+ public abstract int getPropertyId();
+
+ /** Returns one of area Ids in {@link android.car.VehicleAreaSeat}. */
+ public abstract int getAreaId();
+
+ /** Get a builder class for {@link PropertyIdAreaId}*/
+ @NonNull
+ @OptIn(markerClass = ExperimentalCarApi.class)
+ public static PropertyIdAreaId.Builder builder() {
+ return new AutoValue_PropertyIdAreaId.Builder().setAreaId(0);
+ }
+
+ /**
+ * A builder for {@link PropertyIdAreaId}
+ */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ /** Sets a property ID for the {@link PropertyIdAreaId}. */
+ @NonNull
+ public abstract PropertyIdAreaId.Builder setPropertyId(int propertyId);
+
+ /** Sets an area Id for the {@link PropertyIdAreaId}. */
+ @NonNull
+ public abstract PropertyIdAreaId.Builder setAreaId(int areaId);
+
+ /** Create an instance of {@link PropertyIdAreaId}. */
+ @NonNull
+ public abstract PropertyIdAreaId build();
+ }
+
+}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyManager.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyManager.java
index 59d7a1c..47ddc93 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyManager.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyManager.java
@@ -18,7 +18,6 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-import android.car.VehicleAreaType;
import android.car.hardware.CarPropertyValue;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -92,20 +91,25 @@
/**
* Submits a request for registering the listener to get property updates.
*
- * @param propertyIds a list of property id in {@link android.car.VehiclePropertyIds}
+ * @param propertyIdsToCarZones a map of property id in {@link android.car.VehiclePropertyIds}
+ * to their CarZones
* @param sampleRate sample rate in Hz
* @param listener {@link OnCarPropertyResponseListener}
* @param executor execute the task for registering properties
- * @throws SecurityException if the application did not grant permissions for
- * registering properties
+ * @throws SecurityException if the application did not grant permissions for
+ * registering properties
*/
@SuppressWarnings("FutureReturnValueIgnored")
- public void submitRegisterListenerRequest(@NonNull List<Integer> propertyIds, float sampleRate,
+ public void submitRegisterListenerRequest(
+ @NonNull Map<Integer, List<CarZone>> propertyIdsToCarZones, float sampleRate,
@NonNull OnCarPropertyResponseListener listener, @NonNull Executor executor) {
+ List<PropertyIdAreaId> propertyIdWithAreaIds =
+ PropertyUtils.getPropertyIdWithAreaIds(propertyIdsToCarZones);
+ List<Integer> propertyIds = new ArrayList<>(propertyIdsToCarZones.keySet());
checkPermissions(propertyIds);
long samplingIntervalMs;
synchronized (mLock) {
- mListenerAndResponseCache.putListenerAndPropertyIds(listener, propertyIds);
+ mListenerAndResponseCache.putListenerAndUIds(listener, propertyIdWithAreaIds);
if (sampleRate == 0) {
throw new IllegalArgumentException("Sample rate cannot be zero.");
}
@@ -115,66 +119,70 @@
// register properties
executor.execute(() -> {
- for (int propertyId : propertyIds) {
+ for (PropertyIdAreaId propertyIdAndAreadId : propertyIdWithAreaIds) {
try {
- mPropertyRequestProcessor.registerProperty(propertyId, sampleRate);
+ mPropertyRequestProcessor.registerProperty(propertyIdAndAreadId.getPropertyId(),
+ sampleRate);
} catch (IllegalArgumentException e) {
// the property is not implemented
Log.e(LogTags.TAG_CAR_HARDWARE,
- "Failed to register for property: " + propertyId, e);
- mPropertyProcessorCallback.onErrorEvent(CarInternalError.create(propertyId,
- VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
- CarValue.STATUS_UNIMPLEMENTED));
+ "Failed to register for property: "
+ + propertyIdAndAreadId.getPropertyId(), e);
+ mPropertyProcessorCallback.onErrorEvent(
+ CarInternalError.create(propertyIdAndAreadId.getPropertyId(),
+ propertyIdAndAreadId.getAreaId(),
+ CarValue.STATUS_UNIMPLEMENTED));
} catch (Exception e) {
Log.e(LogTags.TAG_CAR_HARDWARE,
- "Failed to register for property: " + propertyId, e);
- mPropertyProcessorCallback.onErrorEvent(CarInternalError.create(propertyId,
- VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, CarValue.STATUS_UNAVAILABLE));
+ "Failed to register for property: "
+ + propertyIdAndAreadId.getPropertyId(), e);
+ mPropertyProcessorCallback.onErrorEvent(
+ CarInternalError.create(propertyIdAndAreadId.getPropertyId(),
+ propertyIdAndAreadId.getAreaId(), CarValue.STATUS_UNAVAILABLE));
}
}
});
- mScheduledExecutorService.schedule(()-> dispatchResponseWithDelay(listener),
+ mScheduledExecutorService.schedule(() -> dispatchResponseWithDelay(listener),
samplingIntervalMs, TimeUnit.MILLISECONDS);
}
/**
* Submits a request for unregistering the listener to get property updates.
*
- * @param listener {@link OnCarPropertyResponseListener} to be unregistered.
- * @throws IllegalStateException if the listener was not registered yet
- * @throws SecurityException if the application did not grant permissions for
- * unregistering properties
+ * @param listener {@link OnCarPropertyResponseListener} to be unregistered.
+ * @throws IllegalStateException if the listener was not registered yet
+ * @throws SecurityException if the application did not grant permissions for
+ * unregistering properties
*/
public void submitUnregisterListenerRequest(@NonNull OnCarPropertyResponseListener listener) {
- List<Integer> propertyIds;
- List<Integer> propertyIdsToBeUnregistered;
+ List<PropertyIdAreaId> uIdsToBeUnregistered;
synchronized (mLock) {
- propertyIds = mListenerAndResponseCache.getPropertyIdsByListener(listener);
- if (propertyIds == null) {
+ if (mListenerAndResponseCache.getUIdsByListener(listener).isEmpty()) {
throw new IllegalStateException("Listener was not registered yet.");
}
- propertyIdsToBeUnregistered = mListenerAndResponseCache.removeListener(listener);
+ uIdsToBeUnregistered = mListenerAndResponseCache.removeListener(listener);
+ if (uIdsToBeUnregistered.size() == 0) {
+ Log.w(LogTags.TAG_CAR_HARDWARE, "No property was unregistered.");
+ return;
+ }
mListenerToSamplingIntervalMap.remove(listener);
}
- if (propertyIdsToBeUnregistered.size() != 0) {
- mScheduledExecutorService.execute(() -> {
- for (int propertyId : propertyIdsToBeUnregistered) {
- mPropertyRequestProcessor.unregisterProperty(propertyId);
- }
- });
- }
+ mScheduledExecutorService.execute(() -> {
+ for (PropertyIdAreaId uId : uIdsToBeUnregistered) {
+ mPropertyRequestProcessor.unregisterProperty(uId.getPropertyId());
+ }
+ });
}
/**
* Submits {@link CarPropertyResponse} for getting property values.
*
- * @param rawRequests a list of {@link GetPropertyRequest}
- * @param executor executes the expensive operation such as fetching property
- * values from cars
- * @throws SecurityException if the application did not grant permissions for getting
- * property
- *
+ * @param rawRequests a list of {@link GetPropertyRequest}
+ * @param executor executes the expensive operation such as fetching property
+ * values from cars
* @return {@link ListenableFuture} contains a list of {@link CarPropertyResponse}
+ * @throws SecurityException if the application did not grant permissions for getting
+ * property
*/
@NonNull
public ListenableFuture<List<CarPropertyResponse<?>>> submitGetPropertyRequest(
@@ -187,9 +195,8 @@
List<Pair<Integer, Integer>> requests = parseRawRequest(rawRequests);
return CallbackToFutureAdapter.getFuture(completer -> {
// Getting properties' value is expensive operation.
- executor.execute(() ->
- mPropertyRequestProcessor.fetchCarPropertyValues(requests, (values, errors) ->
- completer.set(createResponses(values, errors))));
+ executor.execute(() -> mPropertyRequestProcessor.fetchCarPropertyValues(requests,
+ (values, errors) -> completer.set(createResponses(values, errors))));
return "Get property values done";
});
}
@@ -199,12 +206,12 @@
*
* <p>For on_change properties and error events, dispatches them to listeners without delay.
*
- * @param propertyId property id in {@link android.car.VehiclePropertyIds}
+ * @param uId PropertyIdAreaId Class object.
*/
- void dispatchResponsesWithoutDelay(int propertyId) {
+ void dispatchResponsesWithoutDelay(PropertyIdAreaId uId) {
synchronized (mLock) {
- Set<OnCarPropertyResponseListener> listeners =
- mListenerAndResponseCache.getListenersByPropertyId(propertyId);
+ List<OnCarPropertyResponseListener> listeners =
+ mListenerAndResponseCache.getListenersByUId(uId);
if (listeners == null) {
return;
}
@@ -232,7 +239,7 @@
propertyResponses = mListenerAndResponseCache.getResponsesByListener(listener);
//Schedules for next dispatch
- mScheduledExecutorService.schedule(()-> dispatchResponseWithDelay(listener),
+ mScheduledExecutorService.schedule(() -> dispatchResponseWithDelay(listener),
delayTime, TimeUnit.MILLISECONDS);
}
}
@@ -255,8 +262,9 @@
if (mListenerAndResponseCache.updateResponseIfNeeded(carPropertyValue)) {
int propertyId = carPropertyValue.getPropertyId();
if (PropertyUtils.isOnChangeProperty(propertyId)) {
- mScheduledExecutorService.execute(() ->
- dispatchResponsesWithoutDelay(propertyId));
+ mScheduledExecutorService.execute(() -> dispatchResponsesWithoutDelay(
+ PropertyIdAreaId.builder().setPropertyId(propertyId)
+ .setAreaId(carPropertyValue.getAreaId()).build()));
}
}
}
@@ -266,8 +274,9 @@
public void onErrorEvent(CarInternalError carInternalError) {
synchronized (mLock) {
mListenerAndResponseCache.updateInternalError(carInternalError);
- mScheduledExecutorService.execute(() ->
- dispatchResponsesWithoutDelay(carInternalError.getPropertyId()));
+ mScheduledExecutorService.execute(() -> dispatchResponsesWithoutDelay(
+ PropertyIdAreaId.builder().setPropertyId(carInternalError.getPropertyId())
+ .setAreaId(carInternalError.getAreaId()).build()));
}
}
}
@@ -279,10 +288,9 @@
for (CarPropertyValue<?> value : propertyValues) {
carResponses.add(PropertyUtils.convertPropertyValueToPropertyResponse(value));
}
- for (CarInternalError error: propertyErrors) {
- carResponses.add(CarPropertyResponse.builder()
- .setPropertyId(error.getPropertyId())
- .setStatus(error.getErrorCode()).build());
+ for (CarInternalError error : propertyErrors) {
+ carResponses.add(CarPropertyResponse.builder().setPropertyId(
+ error.getPropertyId()).setStatus(error.getErrorCode()).build());
}
return carResponses;
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyResponseCache.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyResponseCache.java
index c87cd94..8bc9dc7 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyResponseCache.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyResponseCache.java
@@ -17,20 +17,20 @@
package androidx.car.app.hardware.common;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import static androidx.car.app.hardware.common.PropertyUtils.CAR_ZONE_TO_AREA_ID;
import android.car.hardware.CarPropertyValue;
-import android.util.SparseArray;
import androidx.annotation.GuardedBy;
+import androidx.annotation.OptIn;
import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.ExperimentalCarApi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -45,71 +45,70 @@
// key: property Id, value: listener which registered for the key value
@GuardedBy("mLock")
- private final SparseArray<Set<OnCarPropertyResponseListener>> mPropertyIdToListeners =
- new SparseArray<>();
+ private final Map<PropertyIdAreaId, List<OnCarPropertyResponseListener>> mUIdToListeners =
+ new HashMap<>();
// key: listener, value: properties
@GuardedBy("mLock")
- private final Map<OnCarPropertyResponseListener, List<Integer>> mListenerToPropertyIds =
+ private final Map<OnCarPropertyResponseListener, List<PropertyIdAreaId>> mListenerToUIds =
new HashMap<>();
// cache for car property values.
@GuardedBy("mLock")
- private final SparseArray<CarPropertyResponse<?>> mPropertyIdToResponse = new SparseArray<>();
+ private final Map<PropertyIdAreaId, CarPropertyResponse<?>> mUIdToResponse = new HashMap<>();
/**
* Puts the listener and a list of properties that are registered by the listener into cache.
*/
- void putListenerAndPropertyIds(OnCarPropertyResponseListener listener,
- List<Integer> propertyIds) {
+ void putListenerAndUIds(OnCarPropertyResponseListener listener, List<PropertyIdAreaId> uIds) {
synchronized (mLock) {
- mListenerToPropertyIds.put(listener, propertyIds);
- for (int propertyId : propertyIds) {
- Set<OnCarPropertyResponseListener> listenerSet =
- mPropertyIdToListeners.get(propertyId, new HashSet<>());
- listenerSet.add(listener);
- mPropertyIdToListeners.put(propertyId, listenerSet);
+ mListenerToUIds.put(listener, uIds);
+ for (PropertyIdAreaId uId : uIds) {
+ List<OnCarPropertyResponseListener> listenerList =
+ mUIdToListeners.getOrDefault(uId, new ArrayList<>());
+ listenerList.add(listener);
+ mUIdToListeners.put(uId, listenerList);
// add an init value if needed
- if (mPropertyIdToResponse.get(propertyId) == null) {
- mPropertyIdToResponse.put(propertyId,
- CarPropertyResponse.builder().setPropertyId(propertyId)
- .setStatus(CarValue.STATUS_UNKNOWN).build());
+ if (mUIdToResponse.get(uId) == null) {
+ mUIdToResponse.put(uId, CarPropertyResponse.builder()
+ .setPropertyId(uId.getPropertyId())
+ .setStatus(CarValue.STATUS_UNKNOWN).build());
}
}
}
}
/** Returns a list of properties that are registered by the listener. */
- List<Integer> getPropertyIdsByListener(OnCarPropertyResponseListener listener) {
+ List<PropertyIdAreaId> getUIdsByListener(OnCarPropertyResponseListener listener) {
synchronized (mLock) {
- return mListenerToPropertyIds.getOrDefault(listener, Collections.emptyList());
+ return mListenerToUIds.getOrDefault(listener, Collections.emptyList());
}
}
/**
- * Returns a {@link Set} containing all {@link OnCarPropertyResponseListener} registered the
+ * Returns a {@link List} containing all {@link OnCarPropertyResponseListener} registered the
* property.
*/
- Set<OnCarPropertyResponseListener> getListenersByPropertyId(int propertyId) {
+ List<OnCarPropertyResponseListener> getListenersByUId(PropertyIdAreaId uId) {
synchronized (mLock) {
- return mPropertyIdToListeners.get(propertyId);
+ return mUIdToListeners.getOrDefault(uId, new ArrayList<>());
}
}
/** Gets a list of {@link CarPropertyResponse} that need to be dispatched to the listener. */
- List<CarPropertyResponse<?>> getResponsesByListener(
- OnCarPropertyResponseListener listener) {
+ List<CarPropertyResponse<?>> getResponsesByListener(OnCarPropertyResponseListener listener) {
List<CarPropertyResponse<?>> values = new ArrayList<>();
synchronized (mLock) {
- List<Integer> propertyIds = mListenerToPropertyIds.get(listener);
- if (propertyIds == null) {
+ List<PropertyIdAreaId> uIds = mListenerToUIds.get(listener);
+ if (uIds == null) {
return values;
}
- for (int propertyId : propertyIds) {
- // return a response with unknown status if can not find in cache
- CarPropertyResponse<?> propertyResponse = mPropertyIdToResponse.get(propertyId,
- CarPropertyResponse.builder().setPropertyId(propertyId)
+
+ for (PropertyIdAreaId uId : uIds) {
+ // Return a response with unknown status if cannot find in cache.
+ CarPropertyResponse<?> propertyResponse = mUIdToResponse.getOrDefault(uId,
+ CarPropertyResponse.builder().setPropertyId(uId.getPropertyId())
.setStatus(CarValue.STATUS_UNKNOWN).build());
values.add(propertyResponse);
}
@@ -122,22 +121,22 @@
*
* @return a list of property ids that are not registered by any other listener
*/
- List<Integer> removeListener(OnCarPropertyResponseListener listener) {
- List<Integer> propertyWithOutListener = new ArrayList<>();
+ List<PropertyIdAreaId> removeListener(OnCarPropertyResponseListener listener) {
+ List<PropertyIdAreaId> propertyWithOutListener = new ArrayList<>();
synchronized (mLock) {
- List<Integer> propertyIds = mListenerToPropertyIds.get(listener);
- mListenerToPropertyIds.remove(listener);
- if (propertyIds == null) {
+ List<PropertyIdAreaId> uIds = mListenerToUIds.get(listener);
+ mListenerToUIds.remove(listener);
+ if (uIds == null) {
throw new IllegalStateException("Listener is not registered yet");
}
- for (int propertyId : propertyIds) {
- Set<OnCarPropertyResponseListener> listenerSet =
- mPropertyIdToListeners.get(propertyId);
- listenerSet.remove(listener);
- if (listenerSet.isEmpty()) {
- propertyWithOutListener.add(propertyId);
- mPropertyIdToListeners.remove(propertyId);
- mPropertyIdToResponse.remove(propertyId);
+ for (PropertyIdAreaId uId : uIds) {
+ List<OnCarPropertyResponseListener> listenerList = mUIdToListeners.getOrDefault(uId,
+ new ArrayList<>());
+ listenerList.remove(listener);
+ if (listenerList.isEmpty()) {
+ propertyWithOutListener.add(uId);
+ mUIdToListeners.remove(uId);
+ mUIdToResponse.remove(uId);
}
}
}
@@ -147,20 +146,24 @@
/** Returns {@code true} if the value in cache is updated. */
boolean updateResponseIfNeeded(CarPropertyValue<?> propertyValue) {
synchronized (mLock) {
- int propertyId = propertyValue.getPropertyId();
- CarPropertyResponse<?> responseInCache = mPropertyIdToResponse.get(propertyId);
+ PropertyIdAreaId uId = PropertyIdAreaId.builder()
+ .setPropertyId(propertyValue.getPropertyId())
+ .setAreaId(propertyValue.getAreaId()).build();
+
+ CarPropertyResponse<?> responseInCache = mUIdToResponse.get(uId);
+
if (responseInCache == null) {
// the property is unregistered
return false;
}
+
long timestampMs = TimeUnit.MILLISECONDS.convert(propertyValue.getTimestamp(),
TimeUnit.NANOSECONDS);
// CarService can not guarantee the order of events.
if (responseInCache.getTimestampMillis() <= timestampMs) {
- // In V1.1, all properties are global properties.
CarPropertyResponse<?> response =
PropertyUtils.convertPropertyValueToPropertyResponse(propertyValue);
- mPropertyIdToResponse.put(propertyId, response);
+ mUIdToResponse.put(uId, response);
return true;
}
return false;
@@ -168,12 +171,18 @@
}
/** Updates the error event in cache */
+ @OptIn(markerClass = ExperimentalCarApi.class)
void updateInternalError(CarInternalError internalError) {
- CarPropertyResponse<?> response = CarPropertyResponse.builder()
+ List<CarZone> carZones = new ArrayList<CarZone>();
+ carZones.add(CAR_ZONE_TO_AREA_ID.inverse().get(internalError.getAreaId()));
+ CarPropertyResponse<?> response = CarPropertyResponse.builder().setPropertyId(
+ internalError.getPropertyId()).setCarZones(carZones).setStatus(
+ internalError.getErrorCode()).build();
+ PropertyIdAreaId uId = PropertyIdAreaId.builder()
.setPropertyId(internalError.getPropertyId())
- .setStatus(internalError.getErrorCode()).build();
+ .setAreaId(internalError.getAreaId()).build();
synchronized (mLock) {
- mPropertyIdToResponse.put(internalError.getPropertyId(), response);
+ mUIdToResponse.put(uId, response);
}
}
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java
index 9e5e0c5..8d833bb 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java
@@ -27,6 +27,7 @@
import android.car.VehicleAreaType;
import android.car.VehiclePropertyIds;
import android.car.hardware.CarPropertyValue;
+import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -36,12 +37,16 @@
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.hardware.info.AutomotiveCarInfo;
import androidx.car.app.hardware.info.EnergyProfile;
+import androidx.car.app.utils.LogTags;
+
+import com.google.common.collect.ImmutableBiMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -109,37 +114,36 @@
};
@ExperimentalCarApi
- private static final SparseArray<CarZone> AREAID_TO_CARZONE = new SparseArray<CarZone>() {
- {
- append(VehicleAreaSeat.SEAT_ROW_1_LEFT,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_FIRST)
- .setColumn(CarZone.CAR_ZONE_COLUMN_LEFT).build());
- append(VehicleAreaSeat.SEAT_ROW_1_CENTER,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_FIRST)
- .setColumn(CarZone.CAR_ZONE_COLUMN_CENTER).build());
- append(VehicleAreaSeat.SEAT_ROW_1_RIGHT,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_FIRST)
- .setColumn(CarZone.CAR_ZONE_COLUMN_RIGHT).build());
- append(VehicleAreaSeat.SEAT_ROW_2_LEFT,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_SECOND)
- .setColumn(CarZone.CAR_ZONE_COLUMN_LEFT).build());
- append(VehicleAreaSeat.SEAT_ROW_2_CENTER,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_SECOND)
- .setColumn(CarZone.CAR_ZONE_COLUMN_CENTER).build());
- append(VehicleAreaSeat.SEAT_ROW_2_RIGHT,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_SECOND)
- .setColumn(CarZone.CAR_ZONE_COLUMN_RIGHT).build());
- append(VehicleAreaSeat.SEAT_ROW_3_LEFT,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_THIRD)
- .setColumn(CarZone.CAR_ZONE_COLUMN_LEFT).build());
- append(VehicleAreaSeat.SEAT_ROW_3_CENTER,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_THIRD)
- .setColumn(CarZone.CAR_ZONE_COLUMN_CENTER).build());
- append(VehicleAreaSeat.SEAT_ROW_3_RIGHT,
- new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_THIRD)
- .setColumn(CarZone.CAR_ZONE_COLUMN_RIGHT).build());
- }
- };
+ static final ImmutableBiMap<CarZone, Integer> CAR_ZONE_TO_AREA_ID =
+ new ImmutableBiMap.Builder<CarZone, Integer>()
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_FIRST)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_LEFT).build(),
+ VehicleAreaSeat.SEAT_ROW_1_LEFT)
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_FIRST)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_CENTER).build(),
+ VehicleAreaSeat.SEAT_ROW_1_CENTER)
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_FIRST)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_RIGHT).build(),
+ VehicleAreaSeat.SEAT_ROW_1_RIGHT)
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_SECOND)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_LEFT).build(),
+ VehicleAreaSeat.SEAT_ROW_2_LEFT)
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_SECOND)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_CENTER).build(),
+ VehicleAreaSeat.SEAT_ROW_2_CENTER)
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_SECOND)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_RIGHT).build(),
+ VehicleAreaSeat.SEAT_ROW_2_RIGHT)
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_THIRD)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_LEFT).build(),
+ VehicleAreaSeat.SEAT_ROW_3_LEFT)
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_THIRD)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_CENTER).build(),
+ VehicleAreaSeat.SEAT_ROW_3_CENTER)
+ .put(new CarZone.Builder().setRow(CarZone.CAR_ZONE_ROW_THIRD)
+ .setColumn(CarZone.CAR_ZONE_COLUMN_RIGHT).build(),
+ VehicleAreaSeat.SEAT_ROW_3_RIGHT)
+ .buildOrThrow();
// Permissions for writing properties. They are system level permissions.
private static final SparseArray<String> PERMISSION_WRITE_PROPERTY = new SparseArray<String>() {
@@ -300,7 +304,8 @@
if (propertyValue.getAreaId() == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL) {
carZones = Collections.singletonList(CarZone.CAR_ZONE_GLOBAL);
} else {
- carZones = mapAreaIdToCarZones(propertyValue.getAreaId());
+ carZones = Collections.singletonList(CAR_ZONE_TO_AREA_ID.inverse().get(
+ propertyValue.getAreaId()));
}
return CarPropertyResponse.builder().setValue(propertyValue.getValue())
.setPropertyId(propertyValue.getPropertyId())
@@ -382,19 +387,30 @@
}
}
- /**
- * Builds a list of {@link CarZone}s from the {@link CarPropertyValue#getAreaId()}.
- */
@OptIn(markerClass = ExperimentalCarApi.class)
- static List<CarZone> mapAreaIdToCarZones(int areaId) {
- List<CarZone> carZones = new ArrayList<>();
- for (int i = 0; i < AREAID_TO_CARZONE.size(); i++) {
- int key = AREAID_TO_CARZONE.keyAt(i);
- if ((key & areaId) != key) {
- carZones.add(AREAID_TO_CARZONE.valueAt(i));
+ static List<PropertyIdAreaId> getPropertyIdWithAreaIds(Map<Integer, List<CarZone>>
+ propertyIdToCarZones) {
+ List<PropertyIdAreaId> propertyIdWithAreaIds = new ArrayList<>();
+ for (Map.Entry<Integer, List<CarZone>> propertyIdWithCarZones :
+ propertyIdToCarZones.entrySet()) {
+ for (CarZone carZone : propertyIdWithCarZones.getValue()) {
+ if (CAR_ZONE_TO_AREA_ID.containsKey(carZone)) {
+ propertyIdWithAreaIds.add(PropertyIdAreaId.builder()
+ .setAreaId(CAR_ZONE_TO_AREA_ID.get(carZone))
+ .setPropertyId(propertyIdWithCarZones.getKey())
+ .build());
+ } else {
+ Log.w(LogTags.TAG_CAR_HARDWARE,
+ "Could not find area Id for car zone: " + carZone.toString()
+ + " for property: " + propertyIdWithCarZones.getKey());
+ }
}
}
- return carZones;
+ if (propertyIdWithAreaIds.isEmpty()) {
+ throw new IllegalStateException("Could not create uIds for the given property Ids and "
+ + "their corresponding car zones.");
+ }
+ return propertyIdWithAreaIds;
}
private PropertyUtils() {
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
index 0a7f1a0..852387e 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
@@ -30,19 +30,20 @@
import static android.car.VehiclePropertyIds.INFO_MODEL;
import static android.car.VehiclePropertyIds.INFO_MODEL_YEAR;
import static android.car.VehiclePropertyIds.PERF_ODOMETER;
+import static android.car.VehiclePropertyIds.PERF_VEHICLE_SPEED;
+import static android.car.VehiclePropertyIds.PERF_VEHICLE_SPEED_DISPLAY;
import static android.car.VehiclePropertyIds.RANGE_REMAINING;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import static androidx.car.app.hardware.common.CarValueUtils.getCarValue;
import static java.util.Objects.requireNonNull;
-import android.car.VehiclePropertyIds;
import android.os.Build;
import android.util.Log;
import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
@@ -50,6 +51,7 @@
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.hardware.common.CarPropertyResponse;
import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.CarZone;
import androidx.car.app.hardware.common.GetPropertyRequest;
import androidx.car.app.hardware.common.OnCarDataAvailableListener;
import androidx.car.app.hardware.common.OnCarPropertyResponseListener;
@@ -57,11 +59,11 @@
import androidx.car.app.hardware.common.PropertyUtils;
import androidx.car.app.utils.LogTags;
+import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -76,14 +78,16 @@
*/
@RestrictTo(LIBRARY)
public class AutomotiveCarInfo implements CarInfo {
- @VisibleForTesting
- static final List<Integer> ENERGY_LEVEL_REQUEST =
- Arrays.asList(EV_BATTERY_LEVEL, FUEL_LEVEL,
- FUEL_LEVEL_LOW, RANGE_REMAINING,
- DISTANCE_DISPLAY_UNITS, FUEL_VOLUME_DISPLAY_UNITS);
+ static final List<CarZone> GLOBAL_CAR_ZONE = Arrays.asList(getGlobalCarZone());
+
@VisibleForTesting
static final float DEFAULT_SAMPLE_RATE = 5f;
+ @OptIn(markerClass = ExperimentalCarApi.class)
+ private static CarZone getGlobalCarZone() {
+ return CarZone.CAR_ZONE_GLOBAL;
+ }
+
/*
* ELECTRONIC_TOLL_COLLECTION_CARD_STATUS in VehiclePropertyIds. The property is added after
* Android Q.
@@ -94,15 +98,37 @@
public static final int SPEED_DISPLAY_UNIT_ID = 289408516;
private static final float UNKNOWN_CAPACITY = Float.NEGATIVE_INFINITY;
- private static final List<Integer> MILEAGE_REQUEST =
- Arrays.asList(PERF_ODOMETER, DISTANCE_DISPLAY_UNITS);
- static final List<Integer> TOLL_REQUEST =
- Collections.singletonList(TOLL_CARD_STATUS_ID);
- private static final List<Integer> SPEED_REQUEST =
- Arrays.asList(VehiclePropertyIds.PERF_VEHICLE_SPEED,
- VehiclePropertyIds.PERF_VEHICLE_SPEED_DISPLAY, SPEED_DISPLAY_UNIT_ID);
- private static final List<Integer> EV_STATUS_REQUEST = Arrays.asList(EV_CHARGE_PORT_OPEN,
- EV_CHARGE_PORT_CONNECTED);
+ static final ImmutableMap<Integer, List<CarZone>> ENERGY_LEVEL_REQUEST = ImmutableMap.<Integer,
+ List<CarZone>>builder()
+ .put(EV_BATTERY_LEVEL, GLOBAL_CAR_ZONE)
+ .put(FUEL_LEVEL, GLOBAL_CAR_ZONE)
+ .put(FUEL_LEVEL_LOW, GLOBAL_CAR_ZONE)
+ .put(RANGE_REMAINING, GLOBAL_CAR_ZONE)
+ .put(DISTANCE_DISPLAY_UNITS, GLOBAL_CAR_ZONE)
+ .put(FUEL_VOLUME_DISPLAY_UNITS, GLOBAL_CAR_ZONE)
+ .buildKeepingLast();
+ private static final ImmutableMap<Integer, List<CarZone>> MILEAGE_REQUEST =
+ ImmutableMap.<Integer,
+ List<CarZone>>builder()
+ .put(PERF_ODOMETER, GLOBAL_CAR_ZONE)
+ .put(DISTANCE_DISPLAY_UNITS, GLOBAL_CAR_ZONE)
+ .buildKeepingLast();
+ static final ImmutableMap<Integer, List<CarZone>> TOLL_REQUEST = ImmutableMap.<Integer,
+ List<CarZone>>builder()
+ .put(TOLL_CARD_STATUS_ID, GLOBAL_CAR_ZONE)
+ .buildKeepingLast();
+ private static final ImmutableMap<Integer, List<CarZone>> SPEED_REQUEST = ImmutableMap.<Integer,
+ List<CarZone>>builder()
+ .put(PERF_VEHICLE_SPEED, GLOBAL_CAR_ZONE)
+ .put(PERF_VEHICLE_SPEED_DISPLAY, GLOBAL_CAR_ZONE)
+ .put(SPEED_DISPLAY_UNIT_ID, GLOBAL_CAR_ZONE)
+ .buildKeepingLast();
+ private static final ImmutableMap<Integer, List<CarZone>> EV_STATUS_REQUEST =
+ ImmutableMap.<Integer,
+ List<CarZone>>builder()
+ .put(EV_CHARGE_PORT_OPEN, GLOBAL_CAR_ZONE)
+ .put(EV_CHARGE_PORT_CONNECTED, GLOBAL_CAR_ZONE)
+ .buildKeepingLast();
private final Map<OnCarDataAvailableListener<?>, OnCarPropertyResponseListener> mListenerMap =
new HashMap<>();
private final PropertyManager mPropertyManager;
@@ -227,12 +253,6 @@
removeListenerImpl(listener);
}
- static <T> CarValue<T> getCarValue(CarPropertyResponse<?> response, @Nullable T value) {
- long timestampMillis = response.getTimestampMillis();
- int status = response.getStatus();
- return new CarValue<>(value, timestampMillis, status);
- }
-
void getCapacitiesThenEnergyLevel(@NonNull Executor executor,
@NonNull OnCarDataAvailableListener<EnergyLevel> listener) {
// Prepare request GetPropertyRequest for battery and fuel capacities.
@@ -438,10 +458,10 @@
CarValue<Integer> displayUnitValue = CarValue.UNKNOWN_INTEGER;
for (CarPropertyResponse<?> response : carPropertyResponses) {
switch (response.getPropertyId()) {
- case VehiclePropertyIds.PERF_VEHICLE_SPEED:
+ case PERF_VEHICLE_SPEED:
rawSpeedValue = getCarValue(response, (Float) response.getValue());
break;
- case VehiclePropertyIds.PERF_VEHICLE_SPEED_DISPLAY:
+ case PERF_VEHICLE_SPEED_DISPLAY:
displaySpeedValue = getCarValue(response, (Float) response.getValue());
break;
case SPEED_DISPLAY_UNIT_ID:
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/climate/AutomotiveCarClimateTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/climate/AutomotiveCarClimateTest.java
new file mode 100644
index 0000000..c09f1e7
--- /dev/null
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/climate/AutomotiveCarClimateTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2022 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 androidx.car.app.hardware.climate;
+
+import static android.car.VehiclePropertyIds.HVAC_POWER_ON;
+
+import static androidx.car.app.hardware.climate.AutomotiveCarClimate.DEFAULT_SAMPLE_RATE_HZ;
+import static androidx.car.app.hardware.climate.ClimateProfileRequest.FEATURE_HVAC_POWER;
+import static androidx.car.app.hardware.common.CarValue.STATUS_SUCCESS;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.car.Car;
+import android.car.hardware.property.CarPropertyManager;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.hardware.common.CarPropertyResponse;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.CarZone;
+import androidx.car.app.hardware.common.OnCarPropertyResponseListener;
+import androidx.car.app.hardware.common.PropertyManager;
+import androidx.car.app.shadows.car.ShadowCar;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowCar.class})
+@DoNotInstrument
+public class AutomotiveCarClimateTest {
+ private List<CarPropertyResponse<?>> mResponse;
+ private CountDownLatch mCountDownLatch;
+ private final Executor mExecutor = directExecutor();
+ private AutomotiveCarClimate mAutomotiveCarClimate;
+ private CarZone mCarZone;
+ @Mock
+ private Car mCarMock;
+ @Mock
+ private CarPropertyManager mCarPropertyManagerMock;
+ @Mock
+ private PropertyManager mPropertyManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowCar.setCar(mCarMock);
+ when(mCarMock.getCarManager(anyString())).thenReturn(mCarPropertyManagerMock);
+ mAutomotiveCarClimate = new AutomotiveCarClimate(mPropertyManager);
+ mCountDownLatch = new CountDownLatch(1);
+ mResponse = new ArrayList<>();
+ mCarZone = new CarZone.Builder().build();
+ }
+
+ @Test
+ public void getHvacPower_verifyResponse() throws InterruptedException {
+ CarClimateFeature.Builder mCarClimateBuilder = new CarClimateFeature.Builder(
+ FEATURE_HVAC_POWER);
+ mCarClimateBuilder.addCarZones(mCarZone);
+ CarClimateFeature mCarClimateFeature = new CarClimateFeature(mCarClimateBuilder);
+ RegisterClimateStateRequest.Builder builder =
+ new RegisterClimateStateRequest.Builder(false);
+ builder.addClimateRegisterFeatures(mCarClimateFeature);
+
+ AtomicReference<CarValue<Boolean>> loadedResult = new AtomicReference<>();
+ CarClimateStateCallback listener = new CarClimateStateCallback() {
+ @Override
+ public void onHvacPowerStateAvailable(@NonNull CarValue<Boolean> hvacPowerState) {
+ loadedResult.set(hvacPowerState);
+ mCountDownLatch.countDown();
+ }
+ };
+
+ mAutomotiveCarClimate.registerClimateStateCallback(mExecutor, builder.build(), listener);
+
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder().put(HVAC_POWER_ON,
+ Collections.singletonList(mCarZone)).buildKeepingLast();
+
+ ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
+ OnCarPropertyResponseListener.class);
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
+ eq(DEFAULT_SAMPLE_RATE_HZ), captor.capture(), eq(mExecutor));
+
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(HVAC_POWER_ON).setCarZones(
+ Collections.singletonList(mCarZone)).setValue(true).setStatus(
+ STATUS_SUCCESS).build());
+
+ captor.getValue().onCarPropertyResponses(mResponse);
+ mCountDownLatch.await();
+
+ CarValue<Boolean> carValue = loadedResult.get();
+ assertThat(carValue.getValue()).isEqualTo(true);
+ assertThat(carValue.getCarZones()).isEqualTo(Collections.singletonList(mCarZone));
+ assertThat(carValue.getStatus()).isEqualTo(STATUS_SUCCESS);
+ }
+}
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyResponseCacheTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyResponseCacheTest.java
index 6ebff47..9ab708d 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyResponseCacheTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyResponseCacheTest.java
@@ -37,9 +37,13 @@
@Test
public void getResponsesByListener_returnsUnknownResponseIfNotInCache() {
Integer testPropertyId = 1;
+ PropertyIdAreaId testPropertyIdAreaIds = PropertyIdAreaId.builder()
+ .setAreaId(0)
+ .setPropertyId(testPropertyId)
+ .build();
PropertyResponseCache propertyResponseCache = new PropertyResponseCache();
- propertyResponseCache.putListenerAndPropertyIds(mOnCarPropertyResponseListener,
- ImmutableList.of(testPropertyId));
+ propertyResponseCache.putListenerAndUIds(mOnCarPropertyResponseListener,
+ ImmutableList.of(testPropertyIdAreaIds));
List<CarPropertyResponse<?>> carPropertyResponses =
propertyResponseCache.getResponsesByListener(mOnCarPropertyResponseListener);
assertThat(carPropertyResponses).containsExactly(
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
index dd78771..5f26962 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
@@ -67,6 +67,7 @@
import androidx.car.app.hardware.common.PropertyManager;
import androidx.car.app.shadows.car.ShadowCar;
+import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -81,8 +82,10 @@
import org.robolectric.annotation.internal.DoNotInstrument;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
@@ -100,6 +103,7 @@
private CountDownLatch mCountDownLatch;
private final Executor mExecutor = directExecutor();
private AutomotiveCarInfo mAutomotiveCarInfo;
+ private List<CarZone> mCarZones;
@Mock
private Car mCarMock;
@Mock
@@ -119,6 +123,7 @@
mGetPropertyRequests = new ArrayList<>();
mPropertyIds = new ArrayList<>();
mResponse = new ArrayList<>();
+ mCarZones = Arrays.asList(CarZone.CAR_ZONE_GLOBAL);
}
@Test
@@ -153,33 +158,24 @@
mGetPropertyRequests.add(GetPropertyRequest.create(INFO_MODEL_YEAR));
// Add "make", "model", "year" values to the response.
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_MAKE)
- .setStatus(STATUS_SUCCESS)
- .setValue("Toy Vehicle")
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_MODEL)
- .setStatus(STATUS_SUCCESS)
- .setValue("Speedy Model")
- .setTimestampMillis(2L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_MODEL_YEAR)
- .setStatus(STATUS_SUCCESS)
- .setValue(2020)
- .setTimestampMillis(3L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(INFO_MAKE).setStatus(
+ STATUS_SUCCESS).setValue("Toy Vehicle").setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(INFO_MODEL).setStatus(
+ STATUS_SUCCESS).setValue("Speedy Model").setTimestampMillis(2L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(INFO_MODEL_YEAR).setStatus(
+ STATUS_SUCCESS).setValue(2020).setTimestampMillis(3L).build());
ListenableFuture<List<CarPropertyResponse<?>>> listenableCarPropertyResponse =
Futures.immediateFuture(mResponse);
- when(mPropertyManager.submitGetPropertyRequest(
- eq(mGetPropertyRequests), eq(mExecutor))).thenReturn(listenableCarPropertyResponse);
+ when(mPropertyManager.submitGetPropertyRequest(eq(mGetPropertyRequests),
+ eq(mExecutor))).thenReturn(listenableCarPropertyResponse);
AtomicReference<Model> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<Model> listener = (data) -> {
loadedResult.set(data);
mCountDownLatch.countDown();
};
mAutomotiveCarInfo.fetchModel(mExecutor, listener);
- verify(mPropertyManager, times(1)).submitGetPropertyRequest(
- eq(mGetPropertyRequests), eq(mExecutor));
+ verify(mPropertyManager, times(1)).submitGetPropertyRequest(eq(mGetPropertyRequests),
+ eq(mExecutor));
mCountDownLatch.await();
Model mModel = loadedResult.get();
assertThat(mModel.getName().getValue()).isEqualTo("Speedy Model");
@@ -201,21 +197,12 @@
mGetPropertyRequests.add(GetPropertyRequest.create(INFO_MODEL));
// Add "make", "model", "year" values to the response.
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_MAKE)
- .setStatus(STATUS_SUCCESS)
- .setValue("Toy Vehicle")
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_MODEL)
- .setStatus(STATUS_SUCCESS)
- .setValue("Speedy Model")
- .setTimestampMillis(2L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_MODEL_YEAR)
- .setStatus(STATUS_SUCCESS)
- .setValue(2020)
- .setTimestampMillis(3L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(INFO_MAKE).setStatus(
+ STATUS_SUCCESS).setValue("Toy Vehicle").setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(INFO_MODEL).setStatus(
+ STATUS_SUCCESS).setValue("Speedy Model").setTimestampMillis(2L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(INFO_MODEL_YEAR).setStatus(
+ STATUS_SUCCESS).setValue(2020).setTimestampMillis(3L).build());
ListenableFuture<List<CarPropertyResponse<?>>> listenableCarPropertyResponse =
Futures.immediateFuture(mResponse);
when(mPropertyManager.submitGetPropertyRequest(
@@ -225,8 +212,8 @@
// Given that the number of values in the response is more(3) than what was requested(2),
// there should be null pointer exception.
- assertThrows(NullPointerException.class, () ->
- mAutomotiveCarInfo.fetchModel(mExecutor, listener));
+ assertThrows(NullPointerException.class,
+ () -> mAutomotiveCarInfo.fetchModel(mExecutor, listener));
}
@Test
@@ -239,36 +226,31 @@
mGetPropertyRequests.add(GetPropertyRequest.create(INFO_FUEL_TYPE));
// Add "evConnector" and "fuel" type of the vehicle to the response.
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_EV_CONNECTOR_TYPE)
- .setStatus(STATUS_SUCCESS)
- .setValue(new Integer[]{chademoInVehicle})
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_FUEL_TYPE)
- .setStatus(STATUS_SUCCESS)
- .setValue(new Integer[]{FUEL_TYPE_UNLEADED})
- .setTimestampMillis(2L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(INFO_EV_CONNECTOR_TYPE).setStatus(
+ STATUS_SUCCESS).setValue(new Integer[]{chademoInVehicle}).setTimestampMillis(
+ 1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(INFO_FUEL_TYPE).setStatus(
+ STATUS_SUCCESS).setValue(new Integer[]{FUEL_TYPE_UNLEADED}).setTimestampMillis(
+ 2L).build());
ListenableFuture<List<CarPropertyResponse<?>>> listenableCarPropertyResponse =
Futures.immediateFuture(mResponse);
- when(mPropertyManager.submitGetPropertyRequest(
- eq(mGetPropertyRequests), eq(mExecutor))).thenReturn(listenableCarPropertyResponse);
+ when(mPropertyManager.submitGetPropertyRequest(eq(mGetPropertyRequests),
+ eq(mExecutor))).thenReturn(listenableCarPropertyResponse);
AtomicReference<EnergyProfile> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<EnergyProfile> listener = (data) -> {
loadedResult.set(data);
mCountDownLatch.countDown();
};
mAutomotiveCarInfo.fetchEnergyProfile(mExecutor, listener);
- verify(mPropertyManager, times(1)).submitGetPropertyRequest(
- eq(mGetPropertyRequests), eq(mExecutor));
+ verify(mPropertyManager, times(1)).submitGetPropertyRequest(eq(mGetPropertyRequests),
+ eq(mExecutor));
mCountDownLatch.await();
EnergyProfile energyProfile = loadedResult.get();
List<Integer> evConnector = new ArrayList<Integer>();
evConnector.add(EVCONNECTOR_TYPE_CHADEMO);
List<Integer> fuel = new ArrayList<Integer>();
fuel.add(FUEL_TYPE_UNLEADED);
- assertThat(energyProfile.getEvConnectorTypes().getValue()).isEqualTo(
- evConnector);
+ assertThat(energyProfile.getEvConnectorTypes().getValue()).isEqualTo(evConnector);
assertThat(energyProfile.getFuelTypes().getValue()).isEqualTo(fuel);
}
@@ -301,9 +283,12 @@
// VehicleUnit.METER in car service
int meterUnit = 0x21;
- // Create "PERF_ODOMETER" and "DISTANCE_DISPLAY_UNITS" property IDs list.
- mPropertyIds.add(PERF_ODOMETER);
- mPropertyIds.add(DISTANCE_DISPLAY_UNITS);
+ // Create "PERF_ODOMETER" and "DISTANCE_DISPLAY_UNITS" property IDs list with car zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(PERF_ODOMETER, mCarZones)
+ .put(DISTANCE_DISPLAY_UNITS, mCarZones)
+ .buildKeepingLast();
AtomicReference<Mileage> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<Mileage> listener = (data) -> {
@@ -315,19 +300,13 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
- verify(mPropertyManager).submitRegisterListenerRequest(eq(mPropertyIds),
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(PERF_ODOMETER)
- .setStatus(STATUS_SUCCESS)
- .setValue(1f)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(DISTANCE_DISPLAY_UNITS)
- .setStatus(STATUS_SUCCESS)
- .setValue(meterUnit)
- .setTimestampMillis(2L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(PERF_ODOMETER).setStatus(
+ STATUS_SUCCESS).setValue(1f).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(DISTANCE_DISPLAY_UNITS).setStatus(
+ STATUS_SUCCESS).setValue(meterUnit).setTimestampMillis(2L).build());
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -340,9 +319,12 @@
@Test
public void addMileageListener_returnsMileageWithUnknownValuesIfNoResponses()
throws InterruptedException {
- // Create "PERF_ODOMETER" and "DISTANCE_DISPLAY_UNITS" property IDs list.
- mPropertyIds.add(PERF_ODOMETER);
- mPropertyIds.add(DISTANCE_DISPLAY_UNITS);
+ // Create "PERF_ODOMETER" and "DISTANCE_DISPLAY_UNITS" property IDs list with car zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(PERF_ODOMETER, mCarZones)
+ .put(DISTANCE_DISPLAY_UNITS, mCarZones)
+ .buildKeepingLast();
AtomicReference<Mileage> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<Mileage> listener = (data) -> {
loadedResult.set(data);
@@ -353,7 +335,7 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
- verify(mPropertyManager).submitRegisterListenerRequest(eq(mPropertyIds),
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -366,9 +348,12 @@
// VehicleUnit.METER in car service
int meterUnit = 0x21;
- // Create "PERF_ODOMETER" and "DISTANCE_DISPLAY_UNITS" property IDs list.
- mPropertyIds.add(PERF_ODOMETER);
- mPropertyIds.add(DISTANCE_DISPLAY_UNITS);
+ // Create "PERF_ODOMETER" and "DISTANCE_DISPLAY_UNITS" property IDs list with car zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(PERF_ODOMETER, mCarZones)
+ .put(DISTANCE_DISPLAY_UNITS, mCarZones)
+ .buildKeepingLast();
AtomicReference<Mileage> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<Mileage> listener = (data) -> {
@@ -382,18 +367,13 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
verify(mPropertyManager, times(2)).submitRegisterListenerRequest(
- eq(mPropertyIds), eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
+ eq(propertyIdsWithCarZones), eq(DEFAULT_SAMPLE_RATE), captor.capture(),
+ eq(mExecutor));
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(PERF_ODOMETER)
- .setStatus(STATUS_SUCCESS)
- .setValue(1f)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(DISTANCE_DISPLAY_UNITS)
- .setStatus(STATUS_SUCCESS)
- .setValue(meterUnit)
- .setTimestampMillis(2L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(PERF_ODOMETER).setStatus(
+ STATUS_SUCCESS).setValue(1f).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(DISTANCE_DISPLAY_UNITS).setStatus(
+ STATUS_SUCCESS).setValue(meterUnit).setTimestampMillis(2L).build());
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -413,9 +393,12 @@
Executor firstExecutor = directExecutor();
Executor secondExecutor = directExecutor();
- // Create "PERF_ODOMETER" and "DISTANCE_DISPLAY_UNITS" property IDs list.
- mPropertyIds.add(PERF_ODOMETER);
- mPropertyIds.add(DISTANCE_DISPLAY_UNITS);
+ // Create "PERF_ODOMETER" and "DISTANCE_DISPLAY_UNITS" property IDs list with car zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(PERF_ODOMETER, mCarZones)
+ .put(DISTANCE_DISPLAY_UNITS, mCarZones)
+ .buildKeepingLast();
AtomicReference<Mileage> loadedFirstResult = new AtomicReference<>();
AtomicReference<Mileage> loadedSecondResult = new AtomicReference<>();
@@ -436,19 +419,13 @@
OnCarPropertyResponseListener.class);
verify(mPropertyManager, times(1)).submitRegisterListenerRequest(
- eq(mPropertyIds), eq(DEFAULT_SAMPLE_RATE), firstCaptor.capture(),
+ eq(propertyIdsWithCarZones), eq(DEFAULT_SAMPLE_RATE), firstCaptor.capture(),
eq(firstExecutor));
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(PERF_ODOMETER)
- .setStatus(STATUS_SUCCESS)
- .setValue(1f)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(DISTANCE_DISPLAY_UNITS)
- .setStatus(STATUS_SUCCESS)
- .setValue(meterUnit)
- .setTimestampMillis(2L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(PERF_ODOMETER).setStatus(
+ STATUS_SUCCESS).setValue(1f).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(DISTANCE_DISPLAY_UNITS).setStatus(
+ STATUS_SUCCESS).setValue(meterUnit).setTimestampMillis(2L).build());
firstCaptor.getValue().onCarPropertyResponses(mResponse);
@@ -459,9 +436,9 @@
OnCarPropertyResponseListener.class);
// Listener request would be submitted twice by now.
- verify(mPropertyManager, times(2)).submitRegisterListenerRequest(eq(mPropertyIds),
- eq(DEFAULT_SAMPLE_RATE),
- secondCaptor.capture(), eq(secondExecutor));
+ verify(mPropertyManager, times(2)).submitRegisterListenerRequest(
+ eq(propertyIdsWithCarZones), eq(DEFAULT_SAMPLE_RATE), secondCaptor.capture(),
+ eq(secondExecutor));
secondCaptor.getValue().onCarPropertyResponses(mResponse);
firstCountDownLatch.await();
@@ -477,9 +454,13 @@
@Test
public void getEvStatus_verifyResponse() throws InterruptedException {
- // Create "EV_CHARGE_PORT_OPEN" and "EV_CHARGE_PORT_CONNECTED" property IDs list.
- mPropertyIds.add(EV_CHARGE_PORT_OPEN);
- mPropertyIds.add(EV_CHARGE_PORT_CONNECTED);
+ // Create "EV_CHARGE_PORT_OPEN" and "EV_CHARGE_PORT_CONNECTED" property IDs list with car
+ // zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(EV_CHARGE_PORT_OPEN, mCarZones)
+ .put(EV_CHARGE_PORT_CONNECTED, mCarZones)
+ .buildKeepingLast();
AtomicReference<EvStatus> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<EvStatus> listener = (data) -> {
@@ -491,19 +472,14 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
- verify(mPropertyManager).submitRegisterListenerRequest(eq(mPropertyIds),
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(EV_CHARGE_PORT_OPEN)
- .setStatus(STATUS_SUCCESS)
- .setValue(true)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(EV_CHARGE_PORT_CONNECTED)
- .setStatus(STATUS_SUCCESS)
- .setValue(false)
- .setTimestampMillis(2L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(EV_CHARGE_PORT_OPEN).setStatus(
+ STATUS_SUCCESS).setValue(true).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(
+ EV_CHARGE_PORT_CONNECTED).setStatus(STATUS_SUCCESS).setValue(
+ false).setTimestampMillis(2L).build());
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -516,9 +492,13 @@
@Test
public void addEvStatusListener_returnsEvStatusWithUnknownValuesIfNoResponses()
throws InterruptedException {
- // Create "EV_CHARGE_PORT_OPEN" and "EV_CHARGE_PORT_CONNECTED" property IDs list.
- mPropertyIds.add(EV_CHARGE_PORT_OPEN);
- mPropertyIds.add(EV_CHARGE_PORT_CONNECTED);
+ // Create "EV_CHARGE_PORT_OPEN" and "EV_CHARGE_PORT_CONNECTED" property IDs list with car
+ // zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(EV_CHARGE_PORT_OPEN, mCarZones)
+ .put(EV_CHARGE_PORT_CONNECTED, mCarZones)
+ .buildKeepingLast();
AtomicReference<EvStatus> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<EvStatus> listener = (data) -> {
loadedResult.set(data);
@@ -529,7 +509,7 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
- verify(mPropertyManager).submitRegisterListenerRequest(eq(mPropertyIds),
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -539,9 +519,13 @@
@Test
public void getEvStatus_withInvalidResponse_verifyResponse() throws InterruptedException {
- // Create "EV_CHARGE_PORT_OPEN" and "EV_CHARGE_PORT_CONNECTED" property IDs list.
- mPropertyIds.add(EV_CHARGE_PORT_OPEN);
- mPropertyIds.add(EV_CHARGE_PORT_CONNECTED);
+ // Create "EV_CHARGE_PORT_OPEN" and "EV_CHARGE_PORT_CONNECTED" property IDs list with car
+ // zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(EV_CHARGE_PORT_OPEN, mCarZones)
+ .put(EV_CHARGE_PORT_CONNECTED, mCarZones)
+ .buildKeepingLast();
AtomicReference<EvStatus> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<EvStatus> listener = (data) -> {
@@ -553,7 +537,7 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
- verify(mPropertyManager).submitRegisterListenerRequest(eq(mPropertyIds),
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
mResponse.add(CarPropertyResponse.builder()
@@ -578,8 +562,11 @@
@Config(minSdk = 31)
@Test
public void getTollCard_verifyResponseApi31() throws InterruptedException {
- // Create "TOLL_CARD_STATUS_ID" request property IDs list.
- mPropertyIds.add(TOLL_CARD_STATUS_ID);
+ // Create "TOLL_CARD_STATUS_ID" request property IDs list with car zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(TOLL_CARD_STATUS_ID, mCarZones)
+ .buildKeepingLast();
AtomicReference<TollCard> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<TollCard> listener = (data) -> {
@@ -591,14 +578,12 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
- verify(mPropertyManager).submitRegisterListenerRequest(eq(mPropertyIds),
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(TOLL_CARD_STATUS_ID)
- .setStatus(STATUS_SUCCESS)
- .setValue(TollCard.TOLLCARD_STATE_VALID)
- .setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(TOLL_CARD_STATUS_ID).setStatus(
+ STATUS_SUCCESS).setValue(TollCard.TOLLCARD_STATE_VALID).setTimestampMillis(
+ 1L).build());
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -626,10 +611,13 @@
@Test
public void getSpeed_verifyResponse() throws InterruptedException {
// Create "PERF_VEHICLE_SPEED", "PERF_VEHICLE_SPEED_DISPLAY" and "SPEED_DISPLAY_UNIT_ID"
- // property IDs list.
- mPropertyIds.add(PERF_VEHICLE_SPEED);
- mPropertyIds.add(PERF_VEHICLE_SPEED_DISPLAY);
- mPropertyIds.add(SPEED_DISPLAY_UNIT_ID);
+ // property IDs list with car zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(PERF_VEHICLE_SPEED, mCarZones)
+ .put(PERF_VEHICLE_SPEED_DISPLAY, mCarZones)
+ .put(SPEED_DISPLAY_UNIT_ID, mCarZones)
+ .buildKeepingLast();
float defaultSpeed = 20f;
float defaultRawSpeed = 20.5f;
@@ -647,24 +635,16 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
- verify(mPropertyManager).submitRegisterListenerRequest(eq(mPropertyIds),
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(SPEED_DISPLAY_UNIT_ID)
- .setStatus(STATUS_SUCCESS)
- .setValue(metersPerSec)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(PERF_VEHICLE_SPEED)
- .setStatus(STATUS_SUCCESS)
- .setValue(defaultRawSpeed)
- .setTimestampMillis(2L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(PERF_VEHICLE_SPEED_DISPLAY)
- .setStatus(STATUS_SUCCESS)
- .setValue(defaultSpeed)
- .setTimestampMillis(3L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(SPEED_DISPLAY_UNIT_ID).setStatus(
+ STATUS_SUCCESS).setValue(metersPerSec).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(PERF_VEHICLE_SPEED).setStatus(
+ STATUS_SUCCESS).setValue(defaultRawSpeed).setTimestampMillis(2L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(
+ PERF_VEHICLE_SPEED_DISPLAY).setStatus(STATUS_SUCCESS).setValue(
+ defaultSpeed).setTimestampMillis(3L).build());
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -679,10 +659,13 @@
public void addSpeedListener_returnsSpeedWithUnknownValuesIfNoResponses()
throws InterruptedException {
// Create "PERF_VEHICLE_SPEED", "PERF_VEHICLE_SPEED_DISPLAY" and "SPEED_DISPLAY_UNIT_ID"
- // property IDs list.
- mPropertyIds.add(PERF_VEHICLE_SPEED);
- mPropertyIds.add(PERF_VEHICLE_SPEED_DISPLAY);
- mPropertyIds.add(SPEED_DISPLAY_UNIT_ID);
+ // property IDs list with car zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(PERF_VEHICLE_SPEED, mCarZones)
+ .put(PERF_VEHICLE_SPEED_DISPLAY, mCarZones)
+ .put(SPEED_DISPLAY_UNIT_ID, mCarZones)
+ .buildKeepingLast();
AtomicReference<Speed> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<Speed> listener = (data) -> {
loadedResult.set(data);
@@ -693,7 +676,7 @@
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
- verify(mPropertyManager).submitRegisterListenerRequest(eq(mPropertyIds),
+ verify(mPropertyManager).submitRegisterListenerRequest(eq(propertyIdsWithCarZones),
eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -716,20 +699,14 @@
float fuelCapacity = 120f;
float fuelLevelValue = 50f;
List<CarPropertyResponse<?>> capacities = new ArrayList<>();
- capacities.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_EV_BATTERY_CAPACITY)
- .setStatus(STATUS_SUCCESS)
- .setValue(evBatteryCapacity)
- .setTimestampMillis(1L).build());
- capacities.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_FUEL_CAPACITY)
- .setStatus(STATUS_SUCCESS)
- .setValue(fuelCapacity)
- .setTimestampMillis(1L).build());
- ListenableFuture<List<CarPropertyResponse<?>>> future =
- Futures.immediateFuture(capacities);
- when(mPropertyManager.submitGetPropertyRequest(eq(mGetPropertyRequests), any()))
- .thenReturn(future);
+ capacities.add(CarPropertyResponse.builder().setPropertyId(
+ INFO_EV_BATTERY_CAPACITY).setStatus(STATUS_SUCCESS).setValue(
+ evBatteryCapacity).setTimestampMillis(1L).build());
+ capacities.add(CarPropertyResponse.builder().setPropertyId(INFO_FUEL_CAPACITY).setStatus(
+ STATUS_SUCCESS).setValue(fuelCapacity).setTimestampMillis(1L).build());
+ ListenableFuture<List<CarPropertyResponse<?>>> future = Futures.immediateFuture(capacities);
+ when(mPropertyManager.submitGetPropertyRequest(eq(mGetPropertyRequests), any())).thenReturn(
+ future);
AtomicReference<EnergyLevel> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<EnergyLevel> listener = (data) -> {
@@ -740,49 +717,37 @@
mAutomotiveCarInfo.addEnergyLevelListener(mExecutor, listener);
// Create "EV_BATTERY_LEVEL", "FUEL_LEVEL", "FUEL_LEVEL_LOW", "RANGE_REMAINING",
- // "DISTANCE_DISPLAY_UNITS" and "FUEL_VOLUME_DISPLAY_UNITS" property IDs list.
- mPropertyIds.add(EV_BATTERY_LEVEL);
- mPropertyIds.add(FUEL_LEVEL);
- mPropertyIds.add(FUEL_LEVEL_LOW);
- mPropertyIds.add(RANGE_REMAINING);
- mPropertyIds.add(DISTANCE_DISPLAY_UNITS);
- mPropertyIds.add(FUEL_VOLUME_DISPLAY_UNITS);
+ // "DISTANCE_DISPLAY_UNITS" and "FUEL_VOLUME_DISPLAY_UNITS" property IDs list with car
+ // zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(EV_BATTERY_LEVEL, mCarZones)
+ .put(FUEL_LEVEL, mCarZones)
+ .put(FUEL_LEVEL_LOW, mCarZones)
+ .put(RANGE_REMAINING, mCarZones)
+ .put(DISTANCE_DISPLAY_UNITS, mCarZones)
+ .put(FUEL_VOLUME_DISPLAY_UNITS, mCarZones)
+ .buildKeepingLast();
- verify(mPropertyManager, times(1)).submitGetPropertyRequest(
- eq(mGetPropertyRequests), eq(mExecutor));
+ verify(mPropertyManager, times(1)).submitGetPropertyRequest(eq(mGetPropertyRequests),
+ eq(mExecutor));
verify(mPropertyManager, times(1)).submitRegisterListenerRequest(
- eq(mPropertyIds), eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
+ eq(propertyIdsWithCarZones), eq(DEFAULT_SAMPLE_RATE), captor.capture(),
+ eq(mExecutor));
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(EV_BATTERY_LEVEL)
- .setStatus(STATUS_SUCCESS)
- .setValue(evBatteryLevelValue)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(FUEL_LEVEL)
- .setStatus(STATUS_SUCCESS)
- .setValue(fuelLevelValue)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(FUEL_LEVEL_LOW)
- .setStatus(STATUS_SUCCESS)
- .setValue(true)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(RANGE_REMAINING)
- .setStatus(STATUS_SUCCESS)
- .setValue(5f)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(DISTANCE_DISPLAY_UNITS)
- .setStatus(STATUS_SUCCESS)
- .setValue(meterDistanceUnit)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(FUEL_VOLUME_DISPLAY_UNITS)
- .setStatus(STATUS_SUCCESS)
- .setValue(meterVolumeUnit)
- .setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(EV_BATTERY_LEVEL).setStatus(
+ STATUS_SUCCESS).setValue(evBatteryLevelValue).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(FUEL_LEVEL).setStatus(
+ STATUS_SUCCESS).setValue(fuelLevelValue).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(FUEL_LEVEL_LOW).setStatus(
+ STATUS_SUCCESS).setValue(true).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(RANGE_REMAINING).setStatus(
+ STATUS_SUCCESS).setValue(5f).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(DISTANCE_DISPLAY_UNITS).setStatus(
+ STATUS_SUCCESS).setValue(meterDistanceUnit).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(
+ FUEL_VOLUME_DISPLAY_UNITS).setStatus(STATUS_SUCCESS).setValue(
+ meterVolumeUnit).setTimestampMillis(1L).build());
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -792,10 +757,8 @@
evBatteryLevelValue / evBatteryCapacity * 100);
assertThat(energyLevel.getFuelPercent().getValue()).isEqualTo(
fuelLevelValue / fuelCapacity * 100);
- assertThat(energyLevel.getEnergyIsLow().getValue()).isEqualTo(
- true);
- assertThat(energyLevel.getRangeRemainingMeters().getValue()).isEqualTo(
- 5f);
+ assertThat(energyLevel.getEnergyIsLow().getValue()).isEqualTo(true);
+ assertThat(energyLevel.getRangeRemainingMeters().getValue()).isEqualTo(5f);
assertThat(energyLevel.getDistanceDisplayUnit().getValue()).isEqualTo(2);
assertThat(energyLevel.getFuelVolumeDisplayUnit().getValue()).isEqualTo(201);
}
@@ -820,17 +783,22 @@
verify(mPropertyManager, times(1)).submitGetPropertyRequest(
eq(mGetPropertyRequests), eq(mExecutor));
// Create "EV_BATTERY_LEVEL", "FUEL_LEVEL", "FUEL_LEVEL_LOW", "RANGE_REMAINING",
- // "DISTANCE_DISPLAY_UNITS" and "FUEL_VOLUME_DISPLAY_UNITS" property IDs list.
- mPropertyIds.add(EV_BATTERY_LEVEL);
- mPropertyIds.add(FUEL_LEVEL);
- mPropertyIds.add(FUEL_LEVEL_LOW);
- mPropertyIds.add(RANGE_REMAINING);
- mPropertyIds.add(DISTANCE_DISPLAY_UNITS);
- mPropertyIds.add(FUEL_VOLUME_DISPLAY_UNITS);
+ // "DISTANCE_DISPLAY_UNITS" and "FUEL_VOLUME_DISPLAY_UNITS" property IDs list with car
+ // zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(EV_BATTERY_LEVEL, mCarZones)
+ .put(FUEL_LEVEL, mCarZones)
+ .put(FUEL_LEVEL_LOW, mCarZones)
+ .put(RANGE_REMAINING, mCarZones)
+ .put(DISTANCE_DISPLAY_UNITS, mCarZones)
+ .put(FUEL_VOLUME_DISPLAY_UNITS, mCarZones)
+ .buildKeepingLast();
ArgumentCaptor<OnCarPropertyResponseListener> captor = ArgumentCaptor.forClass(
OnCarPropertyResponseListener.class);
verify(mPropertyManager, times(1)).submitRegisterListenerRequest(
- eq(mPropertyIds), eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
+ eq(propertyIdsWithCarZones), eq(DEFAULT_SAMPLE_RATE), captor.capture(),
+ eq(mExecutor));
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -852,20 +820,14 @@
float fuelCapacity = 120f;
float fuelLevelValue = 50f;
List<CarPropertyResponse<?>> capacities = new ArrayList<>();
- capacities.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_EV_BATTERY_CAPACITY)
- .setStatus(STATUS_UNAVAILABLE)
- .setValue(evBatteryCapacity)
- .setTimestampMillis(1L).build());
- capacities.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_FUEL_CAPACITY)
- .setStatus(STATUS_UNAVAILABLE)
- .setValue(fuelCapacity)
- .setTimestampMillis(1L).build());
- ListenableFuture<List<CarPropertyResponse<?>>> future =
- Futures.immediateFuture(capacities);
- when(mPropertyManager.submitGetPropertyRequest(
- eq(mGetPropertyRequests), any())).thenReturn(future);
+ capacities.add(CarPropertyResponse.builder().setPropertyId(
+ INFO_EV_BATTERY_CAPACITY).setStatus(STATUS_UNAVAILABLE).setValue(
+ evBatteryCapacity).setTimestampMillis(1L).build());
+ capacities.add(CarPropertyResponse.builder().setPropertyId(INFO_FUEL_CAPACITY).setStatus(
+ STATUS_UNAVAILABLE).setValue(fuelCapacity).setTimestampMillis(1L).build());
+ ListenableFuture<List<CarPropertyResponse<?>>> future = Futures.immediateFuture(capacities);
+ when(mPropertyManager.submitGetPropertyRequest(eq(mGetPropertyRequests), any())).thenReturn(
+ future);
AtomicReference<EnergyLevel> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<EnergyLevel> listener = (data) -> {
@@ -876,49 +838,38 @@
mAutomotiveCarInfo.addEnergyLevelListener(mExecutor, listener);
// Create "EV_BATTERY_LEVEL", "FUEL_LEVEL", "FUEL_LEVEL_LOW", "RANGE_REMAINING",
- // "DISTANCE_DISPLAY_UNITS" and "FUEL_VOLUME_DISPLAY_UNITS" property IDs list.
- mPropertyIds.add(EV_BATTERY_LEVEL);
- mPropertyIds.add(FUEL_LEVEL);
- mPropertyIds.add(FUEL_LEVEL_LOW);
- mPropertyIds.add(RANGE_REMAINING);
- mPropertyIds.add(DISTANCE_DISPLAY_UNITS);
- mPropertyIds.add(FUEL_VOLUME_DISPLAY_UNITS);
+ // "DISTANCE_DISPLAY_UNITS" and "FUEL_VOLUME_DISPLAY_UNITS" property IDs list with car
+ // zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(EV_BATTERY_LEVEL, mCarZones)
+ .put(FUEL_LEVEL, mCarZones)
+ .put(FUEL_LEVEL_LOW, mCarZones)
+ .put(RANGE_REMAINING, mCarZones)
+ .put(DISTANCE_DISPLAY_UNITS, mCarZones)
+ .put(FUEL_VOLUME_DISPLAY_UNITS, mCarZones)
+ .buildKeepingLast();
verify(mPropertyManager, times(1)).submitGetPropertyRequest(
- eq(mGetPropertyRequests), eq(mExecutor));
+ eq(mGetPropertyRequests),
+ eq(mExecutor));
verify(mPropertyManager, times(1)).submitRegisterListenerRequest(
- eq(mPropertyIds), eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
+ eq(propertyIdsWithCarZones), eq(DEFAULT_SAMPLE_RATE), captor.capture(),
+ eq(mExecutor));
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(EV_BATTERY_LEVEL)
- .setStatus(STATUS_SUCCESS)
- .setValue(evBatteryLevelValue)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(FUEL_LEVEL)
- .setStatus(STATUS_SUCCESS)
- .setValue(fuelLevelValue)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(FUEL_LEVEL_LOW)
- .setStatus(STATUS_SUCCESS)
- .setValue(true)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(RANGE_REMAINING)
- .setStatus(STATUS_SUCCESS)
- .setValue(5f)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(DISTANCE_DISPLAY_UNITS)
- .setStatus(STATUS_SUCCESS)
- .setValue(meterDistanceUnit)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(FUEL_VOLUME_DISPLAY_UNITS)
- .setStatus(STATUS_SUCCESS)
- .setValue(meterVolumeUnit)
- .setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(EV_BATTERY_LEVEL).setStatus(
+ STATUS_SUCCESS).setValue(evBatteryLevelValue).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(FUEL_LEVEL).setStatus(
+ STATUS_SUCCESS).setValue(fuelLevelValue).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(FUEL_LEVEL_LOW).setStatus(
+ STATUS_SUCCESS).setValue(true).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(RANGE_REMAINING).setStatus(
+ STATUS_SUCCESS).setValue(5f).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(DISTANCE_DISPLAY_UNITS).setStatus(
+ STATUS_SUCCESS).setValue(meterDistanceUnit).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(
+ FUEL_VOLUME_DISPLAY_UNITS).setStatus(STATUS_SUCCESS).setValue(
+ meterVolumeUnit).setTimestampMillis(1L).build());
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -932,10 +883,8 @@
CarValue.UNKNOWN_FLOAT.getValue());
// The other properties should still work without capacity values
- assertThat(energyLevel.getEnergyIsLow().getValue()).isEqualTo(
- true);
- assertThat(energyLevel.getRangeRemainingMeters().getValue()).isEqualTo(
- 5f);
+ assertThat(energyLevel.getEnergyIsLow().getValue()).isEqualTo(true);
+ assertThat(energyLevel.getRangeRemainingMeters().getValue()).isEqualTo(5f);
assertThat(energyLevel.getDistanceDisplayUnit().getValue()).isEqualTo(2);
assertThat(energyLevel.getFuelVolumeDisplayUnit().getValue()).isEqualTo(201);
}
@@ -954,20 +903,14 @@
float fuelCapacity = 120f;
float fuelLevelValue = 50f;
List<CarPropertyResponse<?>> capacities = new ArrayList<>();
- capacities.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_EV_BATTERY_CAPACITY)
- .setStatus(STATUS_SUCCESS)
- .setValue(evBatteryCapacity)
- .setTimestampMillis(1L).build());
- capacities.add(CarPropertyResponse.builder()
- .setPropertyId(INFO_FUEL_CAPACITY)
- .setStatus(STATUS_SUCCESS)
- .setValue(fuelCapacity)
- .setTimestampMillis(1L).build());
- ListenableFuture<List<CarPropertyResponse<?>>> future =
- Futures.immediateFuture(capacities);
- when(mPropertyManager.submitGetPropertyRequest(
- eq(mGetPropertyRequests), any())).thenReturn(future);
+ capacities.add(CarPropertyResponse.builder().setPropertyId(
+ INFO_EV_BATTERY_CAPACITY).setStatus(STATUS_SUCCESS).setValue(
+ evBatteryCapacity).setTimestampMillis(1L).build());
+ capacities.add(CarPropertyResponse.builder().setPropertyId(INFO_FUEL_CAPACITY).setStatus(
+ STATUS_SUCCESS).setValue(fuelCapacity).setTimestampMillis(1L).build());
+ ListenableFuture<List<CarPropertyResponse<?>>> future = Futures.immediateFuture(capacities);
+ when(mPropertyManager.submitGetPropertyRequest(eq(mGetPropertyRequests), any())).thenReturn(
+ future);
AtomicReference<EnergyLevel> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<EnergyLevel> listener = (data) -> {
@@ -978,45 +921,35 @@
mAutomotiveCarInfo.addEnergyLevelListener(mExecutor, listener);
// Create "EV_BATTERY_LEVEL", "FUEL_LEVEL", "FUEL_LEVEL_LOW", "RANGE_REMAINING",
- // "DISTANCE_DISPLAY_UNITS" and "FUEL_VOLUME_DISPLAY_UNITS" property IDs list.
- mPropertyIds.add(EV_BATTERY_LEVEL);
- mPropertyIds.add(FUEL_LEVEL);
- mPropertyIds.add(FUEL_LEVEL_LOW);
- mPropertyIds.add(RANGE_REMAINING);
- mPropertyIds.add(DISTANCE_DISPLAY_UNITS);
- mPropertyIds.add(FUEL_VOLUME_DISPLAY_UNITS);
+ // "DISTANCE_DISPLAY_UNITS" and "FUEL_VOLUME_DISPLAY_UNITS" property IDs list with car
+ // zones.
+ Map<Integer, List<CarZone>> propertyIdsWithCarZones =
+ ImmutableMap.<Integer, List<CarZone>>builder()
+ .put(EV_BATTERY_LEVEL, mCarZones)
+ .put(FUEL_LEVEL, mCarZones)
+ .put(FUEL_LEVEL_LOW, mCarZones)
+ .put(RANGE_REMAINING, mCarZones)
+ .put(DISTANCE_DISPLAY_UNITS, mCarZones)
+ .put(FUEL_VOLUME_DISPLAY_UNITS, mCarZones)
+ .buildKeepingLast();
- verify(mPropertyManager, times(1)).submitGetPropertyRequest(
- eq(mGetPropertyRequests), eq(mExecutor));
+ verify(mPropertyManager, times(1)).submitGetPropertyRequest(eq(mGetPropertyRequests),
+ eq(mExecutor));
verify(mPropertyManager, times(1)).submitRegisterListenerRequest(
- eq(mPropertyIds), eq(DEFAULT_SAMPLE_RATE), captor.capture(), eq(mExecutor));
+ eq(propertyIdsWithCarZones), eq(DEFAULT_SAMPLE_RATE), captor.capture(),
+ eq(mExecutor));
// Missing response for FUEL_VOLUME_DISPLAY_UNITS.
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(EV_BATTERY_LEVEL)
- .setStatus(STATUS_SUCCESS)
- .setValue(evBatteryLevelValue)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(FUEL_LEVEL)
- .setStatus(STATUS_SUCCESS)
- .setValue(fuelLevelValue)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(FUEL_LEVEL_LOW)
- .setStatus(STATUS_SUCCESS)
- .setValue(true)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(RANGE_REMAINING)
- .setStatus(STATUS_SUCCESS)
- .setValue(5f)
- .setTimestampMillis(1L).build());
- mResponse.add(CarPropertyResponse.builder()
- .setPropertyId(DISTANCE_DISPLAY_UNITS)
- .setStatus(STATUS_SUCCESS)
- .setValue(meterDistanceUnit)
- .setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(EV_BATTERY_LEVEL).setStatus(
+ STATUS_SUCCESS).setValue(evBatteryLevelValue).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(FUEL_LEVEL).setStatus(
+ STATUS_SUCCESS).setValue(fuelLevelValue).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(FUEL_LEVEL_LOW).setStatus(
+ STATUS_SUCCESS).setValue(true).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(RANGE_REMAINING).setStatus(
+ STATUS_SUCCESS).setValue(5f).setTimestampMillis(1L).build());
+ mResponse.add(CarPropertyResponse.builder().setPropertyId(DISTANCE_DISPLAY_UNITS).setStatus(
+ STATUS_SUCCESS).setValue(meterDistanceUnit).setTimestampMillis(1L).build());
captor.getValue().onCarPropertyResponses(mResponse);
mCountDownLatch.await();
@@ -1027,10 +960,8 @@
evBatteryLevelValue / evBatteryCapacity * 100);
assertThat(energyLevel.getFuelPercent().getValue()).isEqualTo(
fuelLevelValue / fuelCapacity * 100);
- assertThat(energyLevel.getEnergyIsLow().getValue()).isEqualTo(
- true);
- assertThat(energyLevel.getRangeRemainingMeters().getValue()).isEqualTo(
- 5f);
+ assertThat(energyLevel.getEnergyIsLow().getValue()).isEqualTo(true);
+ assertThat(energyLevel.getRangeRemainingMeters().getValue()).isEqualTo(5f);
assertThat(energyLevel.getDistanceDisplayUnit().getValue()).isEqualTo(2);
}
}