1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.car.hardware.property; 18 19 import static android.car.feature.Flags.FLAG_BATCHED_SUBSCRIPTIONS; 20 import static android.car.feature.Flags.FLAG_SUBSCRIPTION_WITH_RESOLUTION; 21 import static android.car.feature.Flags.FLAG_VARIABLE_UPDATE_RATE; 22 23 import android.annotation.FlaggedApi; 24 import android.annotation.FloatRange; 25 import android.annotation.NonNull; 26 import android.car.hardware.CarPropertyConfig; 27 import android.util.ArraySet; 28 29 import com.android.car.internal.property.InputSanitizationUtils; 30 import com.android.internal.util.Preconditions; 31 32 import java.util.Set; 33 34 /** 35 * Represents the subscription data to {@link CarPropertyManager#subscribePropertyEvents}. To 36 * create a Subscription use Subscription.Builder. 37 */ 38 @FlaggedApi(FLAG_BATCHED_SUBSCRIPTIONS) 39 public final class Subscription { 40 private final int mPropertyId; 41 @FloatRange(from = 0.0, to = 100.0) 42 private final float mUpdateRateHz; 43 private final int[] mAreaIds; 44 private final boolean mVariableUpdateRateEnabled; 45 private final float mResolution; 46 Subscription(@onNull Builder builder)47 private Subscription(@NonNull Builder builder) { 48 mPropertyId = builder.mBuilderPropertyId; 49 mUpdateRateHz = builder.mBuilderUpdateRateHz; 50 mAreaIds = new int[builder.mBuilderAreaIds.size()]; 51 mVariableUpdateRateEnabled = builder.mVariableUpdateRateEnabled; 52 mResolution = builder.mResolution; 53 int index = 0; 54 for (Integer i : builder.mBuilderAreaIds) { 55 mAreaIds[index++] = i; 56 } 57 } 58 59 /** 60 * Gets the {@link CarPropertyManager#SENSOR_RATE_UI} which is 5hz. 61 * 62 * @return {@link CarPropertyManager#SENSOR_RATE_UI} 63 */ getUpdateRateUi()64 public float getUpdateRateUi() { 65 return CarPropertyManager.SENSOR_RATE_UI; 66 } 67 68 /** 69 * Gets the {@link CarPropertyManager#SENSOR_RATE_NORMAL} which is 1hz. 70 * 71 * @return {@link CarPropertyManager#SENSOR_RATE_NORMAL} 72 */ getUpdateRateNormal()73 public float getUpdateRateNormal() { 74 return CarPropertyManager.SENSOR_RATE_NORMAL; 75 } 76 77 /** 78 * Gets the {@link CarPropertyManager#SENSOR_RATE_FAST} which is 10hz. 79 * 80 * @return {@link CarPropertyManager#SENSOR_RATE_FAST} 81 */ getUpdateRateFast()82 public float getUpdateRateFast() { 83 return CarPropertyManager.SENSOR_RATE_FAST; 84 } 85 86 /** 87 * Gets the {@link CarPropertyManager#SENSOR_RATE_FASTEST} which is 100hz. 88 * 89 * @return {@link CarPropertyManager#SENSOR_RATE_FASTEST} 90 */ getUpdateRateFastest()91 public float getUpdateRateFastest() { 92 return CarPropertyManager.SENSOR_RATE_FASTEST; 93 } 94 95 /** 96 * Gets the propertyId set in the Subscription.Builder object. 97 * 98 * @return The propertyId to subscribe to 99 */ getPropertyId()100 public int getPropertyId() { 101 return mPropertyId; 102 } 103 104 /** 105 * Gets the updateRateHz set in the Subscription.Builder object. 106 * 107 * @return The update rate to subscribe to 108 */ getUpdateRateHz()109 public float getUpdateRateHz() { 110 return mUpdateRateHz; 111 } 112 113 /** 114 * Gets all the areaIds added in the Subscription.Builder object. This will return an empty 115 * array if no particular areaIds were added specifically, which means that the client wants to 116 * subscribe to all possible area IDs. 117 * 118 * @return The areaIds to subscribe to, empty array means subscribing to all area IDs. 119 */ 120 @NonNull getAreaIds()121 public int[] getAreaIds() { 122 return mAreaIds.clone(); 123 } 124 125 /** 126 * Gets the variable update rate enabled boolean set in the Subscription.Builder object. 127 * 128 * @return whether variable update rate is enabled. 129 */ 130 @FlaggedApi(FLAG_VARIABLE_UPDATE_RATE) isVariableUpdateRateEnabled()131 public boolean isVariableUpdateRateEnabled() { 132 return mVariableUpdateRateEnabled; 133 } 134 135 /** 136 * Gets the resolution set in the Subscription.Builder object. 137 * 138 * @return the resolution to subscribe to 139 */ 140 @FlaggedApi(FLAG_SUBSCRIPTION_WITH_RESOLUTION) getResolution()141 public float getResolution() { 142 return mResolution; 143 } 144 145 /** 146 * Builder for Subscription 147 */ 148 @FlaggedApi(FLAG_BATCHED_SUBSCRIPTIONS) 149 public static final class Builder { 150 private final int mBuilderPropertyId; 151 private float mBuilderUpdateRateHz = CarPropertyManager.SENSOR_RATE_ONCHANGE; 152 private final Set<Integer> mBuilderAreaIds = new ArraySet<>(); 153 private long mBuilderFieldsSet = 0L; 154 // By default variable update rate is enabled. 155 private boolean mVariableUpdateRateEnabled = true; 156 private float mResolution = 0.0f; 157 Builder(int propertyId)158 public Builder(int propertyId) { 159 this.mBuilderPropertyId = propertyId; 160 } 161 162 /** 163 * Sets the update rate in Hz for continuous property. 164 * 165 * <p>For {@link CarPropertyConfig#VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE} properties, this 166 * operation has no effect and update rate is defaulted to {@code 0}. 167 * 168 * <p>The update rate decides the hardware polling rate (if supported) and will be sanitized 169 * to a range between {@link CarPropertyConfig#getMinSampleRate} and 170 * {@link CarPropertyConfig#getMaxSampleRate}. 171 * 172 * <p>For better system performance, it is recommended to set this to the smallest 173 * reasonable value, e.g. {@link CarPropertyManager.SENSOR_RATE_NORMAL}. 174 * 175 * @param updateRateHz The update rate to set for the given builder 176 * @return The original Builder object. This value cannot be {@code null}. 177 */ 178 @NonNull setUpdateRateHz(@loatRangefrom = 0.0, to = 100.0) float updateRateHz)179 public Builder setUpdateRateHz(@FloatRange(from = 0.0, to = 100.0) float updateRateHz) { 180 Preconditions.checkArgumentInRange(updateRateHz, /* lower= */ 0.0f, 181 /* upper= */ 100.0f, 182 /* valueName= */ "Update rate should be in the range [0.0f, 100.0f]"); 183 mBuilderUpdateRateHz = updateRateHz; 184 return this; 185 } 186 187 /** 188 * Sets the update rate to {@link CarPropertyManager#SENSOR_RATE_UI} 189 * @return The original Builder object. This value cannot be {@code null}. 190 */ 191 @NonNull setUpdateRateUi()192 public Builder setUpdateRateUi() { 193 mBuilderUpdateRateHz = CarPropertyManager.SENSOR_RATE_UI; 194 return this; 195 } 196 197 /** 198 * Sets the update rate to {@link CarPropertyManager#SENSOR_RATE_NORMAL} 199 * @return The original Builder object. This value cannot be {@code null}. 200 */ 201 @NonNull setUpdateRateNormal()202 public Builder setUpdateRateNormal() { 203 mBuilderUpdateRateHz = CarPropertyManager.SENSOR_RATE_NORMAL; 204 return this; 205 } 206 207 /** 208 * Sets the update rate to {@link CarPropertyManager#SENSOR_RATE_FAST} 209 * @return The original Builder object. This value cannot be {@code null}. 210 */ 211 @NonNull setUpdateRateFast()212 public Builder setUpdateRateFast() { 213 mBuilderUpdateRateHz = CarPropertyManager.SENSOR_RATE_FAST; 214 return this; 215 } 216 217 /** 218 * Sets the update rate to {@link CarPropertyManager#SENSOR_RATE_FASTEST} 219 * @return The original Builder object. This value cannot be {@code null}. 220 */ 221 @NonNull setUpdateRateFastest()222 public Builder setUpdateRateFastest() { 223 mBuilderUpdateRateHz = CarPropertyManager.SENSOR_RATE_FASTEST; 224 return this; 225 } 226 227 /** 228 * Adds an areaId for the Subscription being built. If the areaId is already present, then 229 * the operation is ignored. 230 * 231 * @param areaId The areaId to add for the given builder 232 * @return The original Builder object. This value cannot be {@code null}. 233 */ 234 @NonNull addAreaId(int areaId)235 public Builder addAreaId(int areaId) { 236 mBuilderAreaIds.add(areaId); 237 return this; 238 } 239 240 /** 241 * Enables/Disables variable update rate. 242 * 243 * <p>This option is only meaningful for continuous property. 244 * 245 * <p>By default, variable update rate is enabled for all [propId, areaId]s in this options, 246 * unless disabled via this function or not supported for a specific [propId, areaId] 247 * represented by 248 * {@link AreaIdConfig#isVariableUpdateRateSupported} returning {@code false}. 249 * 250 * <p>For better system performance, it is STRONGLY RECOMMENDED NOT TO DISABLE variable 251 * update rate unless the client relies on continuously arriving property update events 252 * (e.g. for system health checking). 253 * 254 * <p>If variable update rate is enabled, then client will receive property 255 * update events only when the property's value changes (a.k.a behaves the same as an 256 * on-change property). 257 * 258 * <p>If variable update rate is disabled, then client will receive all the property 259 * update events based on the update rate even if the events contain the same property 260 * value. 261 * 262 * <p>E.g. a vehicle speed subscribed at 10hz will cause the vehicle hardware to poll 10 263 * times per second. If the vehicle is initially parked at time 0s, and start moving at 264 * speed 1 at time 1s. If variable update rate is enabled, the client will receive one 265 * initial value of speed 0 at time 0s, and one value of speed 1 at time 1s. If variable 266 * update rate is disabled, the client will receive 10 events of speed 0 from 0s to 1s, and 267 * 10 events of speed 1 from 1s to 2s. 268 * 269 * @return The original Builder object. This value cannot be {@code null}. 270 */ 271 @FlaggedApi(FLAG_VARIABLE_UPDATE_RATE) 272 @NonNull setVariableUpdateRateEnabled(boolean enabled)273 public Builder setVariableUpdateRateEnabled(boolean enabled) { 274 mVariableUpdateRateEnabled = enabled; 275 return this; 276 } 277 278 /** 279 * Sets the resolution for a continuous property. 280 * 281 * <p>The resolution defines the number of significant digits the incoming property updates 282 * will be rounded to. For example, when a client subscribes at a resolution of {@code 283 * 0.01}, the system will round incoming property values to the nearest 0.01. Similarly, if 284 * a client subscribes at resolution {@code 10.0}, the client will get property updates 285 * rounded to the nearest 10. As such, registering with a resolution of {@code 0.0} means 286 * that the client is requesting maximum granularity on incoming property updates. By 287 * default, resolution is set to {@code 0.0} for all [propId, areaId]s in this option, 288 * unless set to a different value via this function. 289 * 290 * <p>Clients can only set the resolution to be an integer power of 10 (i.e, {@code 0.01, 291 * 0.1, 1.0, 10.0}). If a client attempts to subscribe at a resolution that does not belong 292 * to this set, an {@link java.lang.IllegalArgumentException} will be thrown. 293 * 294 * <p>This feature works best when used with the variable update rate (VUR) feature. When 295 * VUR is enabled and the client sets a resolution of, for example, {@code 10.0}, then the 296 * client will receive property updates only when the rounded property value changes in the 297 * magnitude of 10. For example, if the client is subscribing to the vehicle property {@link 298 * android.car.VehiclePropertyIds#PERF_VEHICLE_SPEED} at a resolution of {@code 1.0f}, the 299 * system will not propagate a change in vehicle speed from 24.2 to 24.3 up the stack since 300 * it is not required by the client, but will propagate a change from 24.4 to 24.6. 301 * 302 * <p>Including a resolution when used alongside VUR creates a better system performance, 303 * and is thus <strong>STRONGLY RECOMMENDED</strong> to subscribe to a property at the 304 * finest resolution that is required by the client and no more. 305 * 306 * <p>This option is only meaningful for continuous properties, so its value will be ignored 307 * for non-continuous properties. 308 * 309 * @return The original Builder object. This value cannot be {@code null}. 310 * @throws IllegalArgumentException if resolution is not an integer power of 10. 311 */ 312 @FlaggedApi(FLAG_SUBSCRIPTION_WITH_RESOLUTION) 313 @NonNull setResolution(float resolution)314 public Builder setResolution(float resolution) { 315 InputSanitizationUtils.requireIntegerPowerOf10Resolution(resolution); 316 mResolution = resolution; 317 return this; 318 } 319 320 /** 321 * Builds and returns the Subscription 322 * 323 * @throws IllegalStateException if build is used more than once. 324 */ 325 @NonNull build()326 public Subscription build() { 327 checkNotUsed(); 328 mBuilderFieldsSet |= 0x1; 329 return new Subscription(this); 330 } 331 checkNotUsed()332 private void checkNotUsed() { 333 if ((mBuilderFieldsSet & 0x1) != 0) { 334 throw new IllegalStateException( 335 "This Builder should not be reused. Use a new Builder instance instead"); 336 } 337 } 338 } 339 } 340