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.annotation.NonNull; 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 public static final int UX_RESTRICTIONS_BASELINE = 0; 82 83 // Granular UX Restrictions that are imposed when distraction optimization is required. 84 /** 85 * No dialpad for the purpose of initiating a phone call. 86 */ 87 public static final int UX_RESTRICTIONS_NO_DIALPAD = 1; 88 89 /** 90 * No filtering a list with alpha-numeric character via the use of a character entry method. 91 * 92 * For example, do not allow entering a letter to filter the content of a list down to 93 * items only containing that letter. 94 */ 95 public static final int UX_RESTRICTIONS_NO_FILTERING = 0x1 << 1; 96 97 /** 98 * General purpose strings length cannot exceed the character limit provided by 99 * {@link #getMaxRestrictedStringLength()} 100 */ 101 public static final int UX_RESTRICTIONS_LIMIT_STRING_LENGTH = 0x1 << 2; 102 103 /** 104 * No text entry for the purpose of searching or other manual text string entry actvities. 105 */ 106 public static final int UX_RESTRICTIONS_NO_KEYBOARD = 0x1 << 3; 107 108 /** 109 * No video - no animated frames > 1fps. 110 */ 111 public static final int UX_RESTRICTIONS_NO_VIDEO = 0x1 << 4; 112 113 /** 114 * Limit the number of items user can browse through in total in a single task. 115 * 116 * <p>Refer to {@link #getMaxCumulativeContentItems()} and 117 * {@link #getMaxContentDepth()} for the upper bounds on content 118 * serving. 119 */ 120 public static final int UX_RESTRICTIONS_LIMIT_CONTENT = 0x1 << 5; 121 122 /** 123 * No setup that requires form entry or interaction with external devices. 124 */ 125 public static final int UX_RESTRICTIONS_NO_SETUP = 0x1 << 6; 126 127 /** 128 * No Text Message (SMS, email, conversational, etc.) 129 */ 130 public static final int UX_RESTRICTIONS_NO_TEXT_MESSAGE = 0x1 << 7; 131 132 /** 133 * No text transcription (live or leave behind) of voice can be shown. 134 */ 135 public static final int UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION = 0x1 << 8; 136 137 /** 138 * All restrictions are in effect. 139 */ 140 public static final int UX_RESTRICTIONS_FULLY_RESTRICTED = 141 UX_RESTRICTIONS_NO_DIALPAD | UX_RESTRICTIONS_NO_FILTERING 142 | UX_RESTRICTIONS_LIMIT_STRING_LENGTH | UX_RESTRICTIONS_NO_KEYBOARD 143 | UX_RESTRICTIONS_NO_VIDEO | UX_RESTRICTIONS_LIMIT_CONTENT 144 | UX_RESTRICTIONS_NO_SETUP | UX_RESTRICTIONS_NO_TEXT_MESSAGE 145 | UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION; 146 147 /** @removed accidentally exposed previously */ 148 @IntDef(flag = true, 149 prefix = {"UX_RESTRICTIONS_"}, 150 value = {UX_RESTRICTIONS_BASELINE, 151 UX_RESTRICTIONS_NO_DIALPAD, 152 UX_RESTRICTIONS_NO_FILTERING, 153 UX_RESTRICTIONS_LIMIT_STRING_LENGTH, 154 UX_RESTRICTIONS_NO_KEYBOARD, 155 UX_RESTRICTIONS_NO_VIDEO, 156 UX_RESTRICTIONS_LIMIT_CONTENT, 157 UX_RESTRICTIONS_NO_SETUP, 158 UX_RESTRICTIONS_NO_TEXT_MESSAGE, 159 UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION}) 160 @Retention(RetentionPolicy.SOURCE) 161 public @interface CarUxRestrictionsInfo { 162 } 163 164 private final long mTimeStamp; 165 private final boolean mRequiresDistractionOptimization; 166 @CarUxRestrictionsInfo 167 private final int mActiveRestrictions; 168 // Restriction Parameters 169 private final int mMaxStringLength; 170 private final int mMaxCumulativeContentItems; 171 private final int mMaxContentDepth; 172 173 /** 174 * Builder class for {@link CarUxRestrictions} 175 */ 176 public static class Builder { 177 private final long mTimeStamp; 178 private final boolean mRequiresDistractionOptimization; 179 @CarUxRestrictionsInfo 180 private final int mActiveRestrictions; 181 // Restriction Parameters 182 private int mMaxStringLength = DEFAULT_MAX_LENGTH; 183 private int mMaxCumulativeContentItems = DEFAULT_MAX_CUMULATIVE_ITEMS; 184 private int mMaxContentDepth = DEFAULT_MAX_CONTENT_DEPTH; 185 Builder(boolean reqOpt, @CarUxRestrictionsInfo int restrictions, long time)186 public Builder(boolean reqOpt, @CarUxRestrictionsInfo int restrictions, long time) { 187 mRequiresDistractionOptimization = reqOpt; 188 mActiveRestrictions = restrictions; 189 mTimeStamp = time; 190 } 191 192 /** 193 * Set the maximum length of general purpose strings that can be displayed when 194 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_STRING_LENGTH} is imposed. 195 */ setMaxStringLength(int length)196 public Builder setMaxStringLength(int length) { 197 mMaxStringLength = length; 198 return this; 199 } 200 201 /** 202 * Set the maximum number of cumulative content items that can be displayed when 203 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed. 204 */ setMaxCumulativeContentItems(int number)205 public Builder setMaxCumulativeContentItems(int number) { 206 mMaxCumulativeContentItems = number; 207 return this; 208 } 209 210 /** 211 * Set the maximum number of levels that the user can navigate to when 212 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed. 213 */ setMaxContentDepth(int depth)214 public Builder setMaxContentDepth(int depth) { 215 mMaxContentDepth = depth; 216 return this; 217 } 218 219 /** 220 * Build and return the {@link CarUxRestrictions} object 221 */ build()222 public CarUxRestrictions build() { 223 return new CarUxRestrictions(this); 224 } 225 226 } 227 228 /** 229 * Time at which this UX restriction event was deduced based on the car's driving state. 230 * 231 * @return Elapsed time in nanoseconds since system boot. 232 * 233 * @hide 234 */ getTimeStamp()235 public long getTimeStamp() { 236 return mTimeStamp; 237 } 238 239 /** 240 * Conveys if the foreground activity needs to be distraction optimized. 241 * Activities that can handle distraction optimization need to be tagged as a distraction 242 * optimized in the app's manifest. 243 * <p> 244 * If the app has a foreground activity that has not been distraction optimized, the app has 245 * to switch to another activity that is distraction optimized. Failing that, the system will 246 * stop the foreground activity. 247 * 248 * @return true if distraction optimization is required, false if not 249 */ isRequiresDistractionOptimization()250 public boolean isRequiresDistractionOptimization() { 251 return mRequiresDistractionOptimization; 252 } 253 254 /** 255 * A combination of the Car UX Restrictions that is active for the current state of driving. 256 * 257 * @return A combination of the above {@code @CarUxRestrictionsInfo} 258 */ 259 @CarUxRestrictionsInfo getActiveRestrictions()260 public int getActiveRestrictions() { 261 return mActiveRestrictions; 262 } 263 264 /** 265 * Get the maximum length of general purpose strings that can be displayed when 266 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_STRING_LENGTH} is imposed. 267 * 268 * @return the maximum length of string that can be displayed 269 */ getMaxRestrictedStringLength()270 public int getMaxRestrictedStringLength() { 271 return mMaxStringLength; 272 } 273 274 /** 275 * Get the maximum allowable number of content items that can be displayed to a user during 276 * traversal through any one path in a single task, when 277 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed. 278 * <p> 279 * For example, if a task involving only one view, this represents the maximum allowable number 280 * of content items in this single view. 281 * <p> 282 * However, if the task involves selection of a content item in an originating view that then 283 * surfaces a secondary view to the user, then this value represents the maximum allowable 284 * number of content items between the originating and secondary views combined. 285 * <p> 286 * Specifically, if the maximum allowable value was 60 and a task involved browsing a list of 287 * countries and then viewing the top songs within a country, it would be acceptable to do 288 * either of the following: 289 * <ul> 290 * <li> list 10 countries, and then display the top 50 songs after country selection, or 291 * <li> list 20 countries, and then display the top 40 songs after country selection. 292 * </ul> 293 * <p> 294 * Please refer to this and {@link #getMaxContentDepth()} to know the upper bounds on the 295 * content display when the restriction is in place. 296 * 297 * @return maximum number of cumulative items that can be displayed 298 */ getMaxCumulativeContentItems()299 public int getMaxCumulativeContentItems() { 300 return mMaxCumulativeContentItems; 301 } 302 303 /** 304 * Get the maximum allowable number of content depth levels or view traversals through any one 305 * path in a single task. This is applicable when 306 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed. 307 * <p> 308 * For example, if a task involves only selecting an item from a single list on one view, 309 * the task's content depth would be considered 1. 310 * <p> 311 * However, if the task involves selection of a content item in an originating view that then 312 * surfaces a secondary view to the user, the task's content depth would be considered 2. 313 * <p> 314 * Specifically, if a task involved browsing a list of countries, selecting a genre within the 315 * country, and then viewing the top songs within a country, the task's content depth would be 316 * considered 3. 317 * <p> 318 * Please refer to this and {@link #getMaxCumulativeContentItems()} to know the upper bounds on 319 * the content display when the restriction is in place. 320 * 321 * @return maximum levels deep that the user can navigate to 322 */ getMaxContentDepth()323 public int getMaxContentDepth() { 324 return mMaxContentDepth; 325 } 326 327 @Override 328 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) describeContents()329 public int describeContents() { 330 return 0; 331 } 332 333 @Override writeToParcel(Parcel dest, int flags)334 public void writeToParcel(Parcel dest, int flags) { 335 dest.writeInt(mActiveRestrictions); 336 dest.writeLong(mTimeStamp); 337 dest.writeInt(mRequiresDistractionOptimization ? 1 : 0); 338 dest.writeInt(mMaxStringLength); 339 dest.writeInt(mMaxCumulativeContentItems); 340 dest.writeInt(mMaxContentDepth); 341 } 342 343 public static final Parcelable.Creator<CarUxRestrictions> CREATOR = 344 new Parcelable.Creator<CarUxRestrictions>() { 345 @Override 346 public CarUxRestrictions createFromParcel(Parcel in) { 347 return new CarUxRestrictions(in); 348 } 349 350 @Override 351 public CarUxRestrictions[] newArray(int size) { 352 return new CarUxRestrictions[size]; 353 } 354 }; 355 CarUxRestrictions(CarUxRestrictions uxRestrictions)356 public CarUxRestrictions(CarUxRestrictions uxRestrictions) { 357 mTimeStamp = uxRestrictions.getTimeStamp(); 358 mRequiresDistractionOptimization = uxRestrictions.isRequiresDistractionOptimization(); 359 mActiveRestrictions = uxRestrictions.getActiveRestrictions(); 360 mMaxStringLength = uxRestrictions.mMaxStringLength; 361 mMaxCumulativeContentItems = uxRestrictions.mMaxCumulativeContentItems; 362 mMaxContentDepth = uxRestrictions.mMaxContentDepth; 363 } 364 CarUxRestrictions(Builder builder)365 private CarUxRestrictions(Builder builder) { 366 mTimeStamp = builder.mTimeStamp; 367 mActiveRestrictions = builder.mActiveRestrictions; 368 mRequiresDistractionOptimization = builder.mRequiresDistractionOptimization; 369 mMaxStringLength = builder.mMaxStringLength; 370 mMaxCumulativeContentItems = builder.mMaxCumulativeContentItems; 371 mMaxContentDepth = builder.mMaxContentDepth; 372 } 373 CarUxRestrictions(Parcel in)374 private CarUxRestrictions(Parcel in) { 375 mActiveRestrictions = in.readInt(); 376 mTimeStamp = in.readLong(); 377 mRequiresDistractionOptimization = in.readInt() != 0; 378 mMaxStringLength = in.readInt(); 379 mMaxCumulativeContentItems = in.readInt(); 380 mMaxContentDepth = in.readInt(); 381 } 382 383 @Override toString()384 public String toString() { 385 return "DO: " + mRequiresDistractionOptimization + " UxR: " + mActiveRestrictions 386 + " time: " + mTimeStamp; 387 } 388 389 /** 390 * Returns active restrictions string. 391 * @hide 392 */ 393 @NonNull getActiveRestrictionsString()394 public String getActiveRestrictionsString() { 395 StringBuilder sb = new StringBuilder(); 396 397 if (mActiveRestrictions == UX_RESTRICTIONS_BASELINE) { 398 sb.append("baseline\n"); 399 return sb.toString(); 400 } 401 402 if ((mActiveRestrictions 403 & UX_RESTRICTIONS_NO_DIALPAD) == UX_RESTRICTIONS_NO_DIALPAD) { 404 sb.append("no_dialpad\n"); 405 } 406 407 if ((mActiveRestrictions 408 & UX_RESTRICTIONS_NO_FILTERING) == UX_RESTRICTIONS_NO_FILTERING) { 409 sb.append("no_filtering\n"); 410 } 411 412 if ((mActiveRestrictions 413 & UX_RESTRICTIONS_NO_KEYBOARD) == UX_RESTRICTIONS_NO_KEYBOARD) { 414 sb.append("no_keyboard\n"); 415 } 416 417 if ((mActiveRestrictions 418 & UX_RESTRICTIONS_NO_SETUP) == UX_RESTRICTIONS_NO_SETUP) { 419 sb.append("no_setup\n"); 420 } 421 422 if ((mActiveRestrictions 423 & UX_RESTRICTIONS_NO_TEXT_MESSAGE) == UX_RESTRICTIONS_NO_TEXT_MESSAGE) { 424 sb.append("no_text_message\n"); 425 } 426 427 if ((mActiveRestrictions 428 & UX_RESTRICTIONS_NO_VIDEO) == UX_RESTRICTIONS_NO_VIDEO) { 429 sb.append("no_video\n"); 430 } 431 432 if ((mActiveRestrictions 433 & UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION) 434 == UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION) { 435 sb.append("no_voice_transcription\n"); 436 } 437 return sb.toString(); 438 } 439 440 /** 441 * Compares if the restrictions are the same. Doesn't compare the timestamps. 442 * 443 * @param other the other CarUxRestrictions object 444 * @return true if the restrictions are same, false otherwise 445 */ isSameRestrictions(CarUxRestrictions other)446 public boolean isSameRestrictions(CarUxRestrictions other) { 447 if (other == null) { 448 return false; 449 } 450 if (other == this) { 451 return true; 452 } 453 return other.mRequiresDistractionOptimization == mRequiresDistractionOptimization 454 && other.mActiveRestrictions == mActiveRestrictions; 455 } 456 } 457