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