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