1 /*
2  * Copyright (C) 2018 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.drivingstate;
18 
19 import android.annotation.IntDef;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 
26 /**
27  * Car UX Restrictions event.  This contains information on the set of UX restrictions that is in
28  * place due to the car's driving state.
29  * <p>
30  * The restriction information is organized as follows:
31  * <ul>
32  * <li> When there are no restrictions in place, for example when the car is parked,
33  * <ul>
34  * <li> {@link #isRequiresDistractionOptimization()} returns false.  Apps can display activities
35  * that are not distraction optimized.
36  * <li> When {@link #isRequiresDistractionOptimization()} returns false, apps don't have to call
37  * {@link #getActiveRestrictions()}, since there is no distraction optimization required.
38  * </ul>
39  * <li> When the driving state changes, causing the UX restrictions to come in effect,
40  * <ul>
41  * <li> {@link #isRequiresDistractionOptimization()} returns true.  Apps can only display activities
42  * that are distraction optimized.  Distraction optimized activities must follow the base design
43  * guidelines to ensure a distraction free driving experience for the user.
44  * <li> When {@link #isRequiresDistractionOptimization()} returns true, apps must call
45  * {@link #getActiveRestrictions()}, to get the currently active UX restrictions to adhere to.
46  * {@link #getActiveRestrictions()} provides additional information on the set of UX
47  * restrictions that are in place for the current driving state.
48  * <p>
49  * The UX restrictions returned by {@link #getActiveRestrictions()}, for the same driving state of
50  * the vehicle, could vary depending on the OEM and the market.  For example, when the car is
51  * idling, the set of active UX restrictions will depend on the car maker and the safety standards
52  * of the market that the vehicle is deployed in.
53  * </ul>
54  * </ul>
55  * <p>
56  * Apps that intend to be run when the car is being driven need to
57  * <ul>
58  * <li> Comply with the general distraction optimization guidelines.
59  * <li> Listen and react to the UX restrictions changes as detailed above.  Since the restrictions
60  * could vary depending on the market, apps are expected to react to the restriction information
61  * and not to the absolute driving state.
62  * </ul>
63  */
64 public final class CarUxRestrictions implements Parcelable {
65 
66     // Default fallback values for the restriction related parameters if the information is
67     // not available from the underlying service.
68     private static final int DEFAULT_MAX_LENGTH = 120;
69     private static final int DEFAULT_MAX_CUMULATIVE_ITEMS = 21;
70     private static final int DEFAULT_MAX_CONTENT_DEPTH = 3;
71 
72     /**
73      * No specific restrictions in place, but baseline distraction optimization guidelines need to
74      * be adhered to when {@link #isRequiresDistractionOptimization()} is true.
75      */
76     public static final int UX_RESTRICTIONS_BASELINE = 0;
77 
78     // Granular UX Restrictions that are imposed when distraction optimization is required.
79     /**
80      * No dialpad for the purpose of initiating a phone call.
81      */
82     public static final int UX_RESTRICTIONS_NO_DIALPAD = 1;
83 
84     /**
85      * No filtering a list with alpha-numeric character via the use of a character entry method.
86      *
87      * For example, do not allow entering a letter to filter the content of a list down to
88      * items only containing that letter.
89      */
90     public static final int UX_RESTRICTIONS_NO_FILTERING = 0x1 << 1;
91 
92     /**
93      * General purpose strings length cannot exceed the character limit provided by
94      * {@link #getMaxRestrictedStringLength()}
95      */
96     public static final int UX_RESTRICTIONS_LIMIT_STRING_LENGTH = 0x1 << 2;
97 
98     /**
99      * No text entry for the purpose of searching or other manual text string entry actvities.
100      */
101     public static final int UX_RESTRICTIONS_NO_KEYBOARD = 0x1 << 3;
102 
103     /**
104      * No video - no animated frames > 1fps.
105      */
106     public static final int UX_RESTRICTIONS_NO_VIDEO = 0x1 << 4;
107 
108     /**
109      * Limit the number of items user can browse through in total in a single task.
110      *
111      * <p>Refer to {@link #getMaxCumulativeContentItems()} and
112      * {@link #getMaxContentDepth()} for the upper bounds on content
113      * serving.
114      */
115     public static final int UX_RESTRICTIONS_LIMIT_CONTENT = 0x1 << 5;
116 
117     /**
118      * No setup that requires form entry or interaction with external devices.
119      */
120     public static final int UX_RESTRICTIONS_NO_SETUP = 0x1 << 6;
121 
122     /**
123      * No Text Message (SMS, email, conversational, etc.)
124      */
125     public static final int UX_RESTRICTIONS_NO_TEXT_MESSAGE = 0x1 << 7;
126 
127     /**
128      * No text transcription (live or leave behind) of voice can be shown.
129      */
130     public static final int UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION = 0x1 << 8;
131 
132     /**
133      * All restrictions are in effect.
134      */
135     public static final int UX_RESTRICTIONS_FULLY_RESTRICTED =
136             UX_RESTRICTIONS_NO_DIALPAD | UX_RESTRICTIONS_NO_FILTERING
137                     | UX_RESTRICTIONS_LIMIT_STRING_LENGTH | UX_RESTRICTIONS_NO_KEYBOARD
138                     | UX_RESTRICTIONS_NO_VIDEO | UX_RESTRICTIONS_LIMIT_CONTENT
139                     | UX_RESTRICTIONS_NO_SETUP | UX_RESTRICTIONS_NO_TEXT_MESSAGE
140                     | UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION;
141 
142     @IntDef(flag = true,
143             prefix = {"UX_RESTRICTIONS_"},
144             value = {UX_RESTRICTIONS_BASELINE,
145                     UX_RESTRICTIONS_NO_DIALPAD,
146                     UX_RESTRICTIONS_NO_FILTERING,
147                     UX_RESTRICTIONS_LIMIT_STRING_LENGTH,
148                     UX_RESTRICTIONS_NO_KEYBOARD,
149                     UX_RESTRICTIONS_NO_VIDEO,
150                     UX_RESTRICTIONS_LIMIT_CONTENT,
151                     UX_RESTRICTIONS_NO_SETUP,
152                     UX_RESTRICTIONS_NO_TEXT_MESSAGE,
153                     UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION})
154     @Retention(RetentionPolicy.SOURCE)
155     public @interface CarUxRestrictionsInfo {
156     }
157 
158     private final long mTimeStamp;
159     private final boolean mRequiresDistractionOptimization;
160     @CarUxRestrictionsInfo
161     private final int mActiveRestrictions;
162     // Restriction Parameters
163     private final int mMaxStringLength;
164     private final int mMaxCumulativeContentItems;
165     private final int mMaxContentDepth;
166 
167     /**
168      * Builder class for {@link CarUxRestrictions}
169      */
170     public static class Builder {
171         private final long mTimeStamp;
172         private final boolean mRequiresDistractionOptimization;
173         @CarUxRestrictionsInfo
174         private final int mActiveRestrictions;
175         // Restriction Parameters
176         private int mMaxStringLength = DEFAULT_MAX_LENGTH;
177         private int mMaxCumulativeContentItems = DEFAULT_MAX_CUMULATIVE_ITEMS;
178         private int mMaxContentDepth = DEFAULT_MAX_CONTENT_DEPTH;
179 
Builder(boolean reqOpt, @CarUxRestrictionsInfo int restrictions, long time)180         public Builder(boolean reqOpt, @CarUxRestrictionsInfo int restrictions, long time) {
181             mRequiresDistractionOptimization = reqOpt;
182             mActiveRestrictions = restrictions;
183             mTimeStamp = time;
184         }
185 
186         /**
187          * Set the maximum length of general purpose strings that can be displayed when
188          * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_STRING_LENGTH} is imposed.
189          */
setMaxStringLength(int length)190         public Builder setMaxStringLength(int length) {
191             mMaxStringLength = length;
192             return this;
193         }
194 
195         /**
196          *  Set the maximum number of cumulative content items that can be displayed when
197          * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed.
198          */
setMaxCumulativeContentItems(int number)199         public Builder setMaxCumulativeContentItems(int number) {
200             mMaxCumulativeContentItems = number;
201             return this;
202         }
203 
204         /**
205          * Set the maximum number of levels that the user can navigate to when
206          * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed.
207          */
setMaxContentDepth(int depth)208         public Builder setMaxContentDepth(int depth) {
209             mMaxContentDepth = depth;
210             return this;
211         }
212 
213         /**
214          * Build and return the {@link CarUxRestrictions} object
215          */
build()216         public CarUxRestrictions build() {
217             return new CarUxRestrictions(this);
218         }
219 
220     }
221 
222     /**
223      * Time at which this UX restriction event was deduced based on the car's driving state.
224      *
225      * @return Elapsed time in nanoseconds since system boot.
226      *
227      * @hide
228      */
getTimeStamp()229     public long getTimeStamp() {
230         return mTimeStamp;
231     }
232 
233     /**
234      * Conveys if the foreground activity needs to be distraction optimized.
235      * Activities that can handle distraction optimization need to be tagged as a distraction
236      * optimized in the app's manifest.
237      * <p>
238      * If the app has a foreground activity that has not been distraction optimized, the app has
239      * to switch to another activity that is distraction optimized.  Failing that, the system will
240      * stop the foreground activity.
241      *
242      * @return true if distraction optimization is required, false if not
243      */
isRequiresDistractionOptimization()244     public boolean isRequiresDistractionOptimization() {
245         return mRequiresDistractionOptimization;
246     }
247 
248     /**
249      * A combination of the Car UX Restrictions that is active for the current state of driving.
250      *
251      * @return A combination of the above {@code @CarUxRestrictionsInfo}
252      */
253     @CarUxRestrictionsInfo
getActiveRestrictions()254     public int getActiveRestrictions() {
255         return mActiveRestrictions;
256     }
257 
258     /**
259      * Get the maximum length of general purpose strings that can be displayed when
260      * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_STRING_LENGTH} is imposed.
261      *
262      * @return the maximum length of string that can be displayed
263      */
getMaxRestrictedStringLength()264     public int getMaxRestrictedStringLength() {
265         return mMaxStringLength;
266     }
267 
268     /**
269      * Get the maximum allowable number of content items that can be displayed to a user during
270      * traversal through any one path in a single task, when
271      * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed.
272      * <p>
273      * For example, if a task involving only one view, this represents the maximum allowable number
274      * of content items in this single view.
275      * <p>
276      * However, if the task involves selection of a content item in an originating view that then
277      * surfaces a secondary view to the user, then this value represents the maximum allowable
278      * number of content items between the originating and secondary views combined.
279      * <p>
280      * Specifically, if the maximum allowable value was 60 and a task involved browsing a list of
281      * countries and then viewing the top songs within a country, it would be acceptable to do
282      * either of the following:
283      * <ul>
284      * <li> list 10 countries, and then display the top 50 songs after country selection, or
285      * <li> list 20 countries, and then display the top 40 songs after country selection.
286      * </ul>
287      * <p>
288      * Please refer to this and {@link #getMaxContentDepth()} to know the upper bounds on the
289      * content display when the restriction is in place.
290      *
291      * @return maximum number of cumulative items that can be displayed
292      */
getMaxCumulativeContentItems()293     public int getMaxCumulativeContentItems() {
294         return mMaxCumulativeContentItems;
295     }
296 
297     /**
298      * Get the maximum allowable number of content depth levels or view traversals through any one
299      * path in a single task.  This is applicable when
300      * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed.
301      * <p>
302      * For example, if a task involves only selecting an item from a single list on one view,
303      * the task's content depth would be considered 1.
304      * <p>
305      * However, if the task involves selection of a content item in an originating view that then
306      * surfaces a secondary view to the user, the task's content depth would be considered 2.
307      * <p>
308      * Specifically, if a task involved browsing a list of countries, selecting a genre within the
309      * country, and then viewing the top songs within a country, the task's content depth would be
310      * considered 3.
311      * <p>
312      * Please refer to this and {@link #getMaxCumulativeContentItems()} to know the upper bounds on
313      * the content display when the restriction is in place.
314      *
315      * @return maximum number of cumulative items that can be displayed
316      */
getMaxContentDepth()317     public int getMaxContentDepth() {
318         return mMaxContentDepth;
319     }
320 
321     @Override
describeContents()322     public int describeContents() {
323         return 0;
324     }
325 
326     @Override
writeToParcel(Parcel dest, int flags)327     public void writeToParcel(Parcel dest, int flags) {
328         dest.writeInt(mActiveRestrictions);
329         dest.writeLong(mTimeStamp);
330         dest.writeInt(mRequiresDistractionOptimization ? 1 : 0);
331         dest.writeInt(mMaxStringLength);
332         dest.writeInt(mMaxCumulativeContentItems);
333         dest.writeInt(mMaxContentDepth);
334     }
335 
336     public static final Parcelable.Creator<CarUxRestrictions> CREATOR =
337             new Parcelable.Creator<CarUxRestrictions>() {
338         @Override
339         public CarUxRestrictions createFromParcel(Parcel in) {
340             return new CarUxRestrictions(in);
341         }
342 
343         @Override
344         public CarUxRestrictions[] newArray(int size) {
345             return new CarUxRestrictions[size];
346         }
347     };
348 
CarUxRestrictions(CarUxRestrictions uxRestrictions)349     public CarUxRestrictions(CarUxRestrictions uxRestrictions) {
350         mTimeStamp = uxRestrictions.getTimeStamp();
351         mRequiresDistractionOptimization = uxRestrictions.isRequiresDistractionOptimization();
352         mActiveRestrictions = uxRestrictions.getActiveRestrictions();
353         mMaxStringLength = uxRestrictions.mMaxStringLength;
354         mMaxCumulativeContentItems = uxRestrictions.mMaxCumulativeContentItems;
355         mMaxContentDepth = uxRestrictions.mMaxContentDepth;
356     }
357 
CarUxRestrictions(Builder builder)358     private CarUxRestrictions(Builder builder) {
359         mTimeStamp = builder.mTimeStamp;
360         mActiveRestrictions = builder.mActiveRestrictions;
361         mRequiresDistractionOptimization = builder.mRequiresDistractionOptimization;
362         mMaxStringLength = builder.mMaxStringLength;
363         mMaxCumulativeContentItems = builder.mMaxCumulativeContentItems;
364         mMaxContentDepth = builder.mMaxContentDepth;
365     }
366 
CarUxRestrictions(Parcel in)367     private CarUxRestrictions(Parcel in) {
368         mActiveRestrictions = in.readInt();
369         mTimeStamp = in.readLong();
370         mRequiresDistractionOptimization = in.readInt() != 0;
371         mMaxStringLength = in.readInt();
372         mMaxCumulativeContentItems = in.readInt();
373         mMaxContentDepth = in.readInt();
374     }
375 
376     @Override
toString()377     public String toString() {
378         return "DO: " + mRequiresDistractionOptimization + " UxR: " + mActiveRestrictions
379                 + " time: " + mTimeStamp;
380     }
381 
382     /**
383      * Compares if the restrictions are the same.  Doesn't compare the timestamps.
384      *
385      * @param other the other CarUxRestrictions object
386      * @return true if the restrictions are same, false otherwise
387      */
isSameRestrictions(CarUxRestrictions other)388     public boolean isSameRestrictions(CarUxRestrictions other) {
389         if (other == null) {
390             return false;
391         }
392         if (other == this) {
393             return true;
394         }
395         return other.mRequiresDistractionOptimization == mRequiresDistractionOptimization
396                 && other.mActiveRestrictions == mActiveRestrictions;
397     }
398 }
399