1 /* 2 * Copyright (C) 2022 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.oem; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.car.annotation.ApiRequirements; 25 import android.car.media.CarVolumeGroupInfo; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 29 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.util.Preconditions; 32 33 import java.util.ArrayList; 34 import java.util.List; 35 import java.util.Objects; 36 37 /** 38 * Class to encapsulate the audio focus evaluation to the OEM audio service 39 * 40 * @hide 41 */ 42 @SystemApi 43 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 44 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 45 public final class OemCarAudioFocusEvaluationRequest implements Parcelable { 46 47 private @Nullable final AudioFocusEntry mAudioFocusRequest; 48 private @NonNull final List<CarVolumeGroupInfo> mMutedVolumeGroups; 49 private @NonNull final List<AudioFocusEntry> mFocusHolders; 50 private @NonNull final List<AudioFocusEntry> mFocusLosers; 51 private final int mAudioZoneId; 52 53 /** 54 * @hide 55 */ 56 @VisibleForTesting OemCarAudioFocusEvaluationRequest(Parcel in)57 public OemCarAudioFocusEvaluationRequest(Parcel in) { 58 byte flg = in.readByte(); 59 mAudioFocusRequest = (flg & Builder.FOCUS_REQUEST_FIELDS_SET) == 0 60 ? null : AudioFocusEntry.CREATOR.createFromParcel(in); 61 mMutedVolumeGroups = new ArrayList<>(); 62 in.readParcelableList(mMutedVolumeGroups, CarVolumeGroupInfo.class.getClassLoader()); 63 mFocusHolders = new ArrayList<>(); 64 in.readParcelableList(mFocusHolders, AudioFocusEntry.class.getClassLoader()); 65 mFocusLosers = new ArrayList<>(); 66 in.readParcelableList(mFocusLosers, AudioFocusEntry.class.getClassLoader()); 67 mAudioZoneId = in.readInt(); 68 } 69 70 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 71 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 72 @NonNull 73 public static final Creator<OemCarAudioFocusEvaluationRequest> CREATOR = 74 new Creator<>() { 75 @Override 76 public OemCarAudioFocusEvaluationRequest createFromParcel(Parcel in) { 77 return new OemCarAudioFocusEvaluationRequest(in); 78 } 79 80 @Override 81 public OemCarAudioFocusEvaluationRequest[] newArray(int size) { 82 return new OemCarAudioFocusEvaluationRequest[size]; 83 } 84 }; 85 86 87 @Override 88 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 89 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 90 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) describeContents()91 public int describeContents() { 92 return 0; 93 } 94 95 @Override 96 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 97 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 98 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) writeToParcel(@onNull Parcel dest, int flags)99 public void writeToParcel(@NonNull Parcel dest, int flags) { 100 byte flg = 0; 101 if (mAudioFocusRequest != null) { 102 flg = (byte) (flg | Builder.FOCUS_REQUEST_FIELDS_SET); 103 } 104 dest.writeByte(flg); 105 if (mAudioFocusRequest != null) { 106 mAudioFocusRequest.writeToParcel(dest, flags); 107 } 108 dest.writeParcelableList(mMutedVolumeGroups, flags); 109 dest.writeParcelableList(mFocusHolders, flags); 110 dest.writeParcelableList(mFocusLosers, flags); 111 dest.writeInt(mAudioZoneId); 112 } 113 114 /** 115 * Returns the audio zone id for the request 116 */ 117 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 118 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getAudioZoneId()119 public int getAudioZoneId() { 120 return mAudioZoneId; 121 } 122 123 /** 124 * Returns the current audio focus info to evaluate, 125 * in cases where the audio focus info is null 126 * the request is to re-evaluate current focus holder and losers. 127 */ 128 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 129 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getAudioFocusRequest()130 public @Nullable AudioFocusEntry getAudioFocusRequest() { 131 return mAudioFocusRequest; 132 } 133 134 /** 135 * Returns the currently muted volume groups 136 */ 137 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 138 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getMutedVolumeGroups()139 public @NonNull List<CarVolumeGroupInfo> getMutedVolumeGroups() { 140 return mMutedVolumeGroups; 141 } 142 143 /** 144 * Returns the current focus holder 145 */ 146 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 147 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getFocusHolders()148 public @NonNull List<AudioFocusEntry> getFocusHolders() { 149 return mFocusHolders; 150 } 151 152 /** 153 * Returns the current focus losers (.i.e focus request that have transiently lost focus) 154 */ 155 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 156 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getFocusLosers()157 public @NonNull List<AudioFocusEntry> getFocusLosers() { 158 return mFocusLosers; 159 } 160 161 @Override 162 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 163 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) equals(Object o)164 public boolean equals(Object o) { 165 if (this == o) { 166 return true; 167 } 168 169 if (!(o instanceof OemCarAudioFocusEvaluationRequest)) { 170 return false; 171 } 172 173 OemCarAudioFocusEvaluationRequest that = (OemCarAudioFocusEvaluationRequest) o; 174 175 return safeEquals(mAudioFocusRequest, that.mAudioFocusRequest) 176 && mFocusHolders.equals(that.mFocusHolders) 177 && mFocusLosers.equals(that.mFocusLosers) 178 && mMutedVolumeGroups.equals(that.mMutedVolumeGroups) 179 && mAudioZoneId == that.mAudioZoneId; 180 } 181 182 @Override 183 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 184 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) hashCode()185 public int hashCode() { 186 return Objects.hash(mAudioFocusRequest, mFocusHolders, mFocusLosers, mMutedVolumeGroups, 187 mAudioZoneId); 188 } 189 190 /** 191 * @hide 192 */ 193 @VisibleForTesting OemCarAudioFocusEvaluationRequest( @ullable AudioFocusEntry audioFocusEntry, @NonNull List<CarVolumeGroupInfo> mutedVolumeGroups, @NonNull List<AudioFocusEntry> focusHolders, @NonNull List<AudioFocusEntry> focusLosers, int audioZoneId)194 public OemCarAudioFocusEvaluationRequest( 195 @Nullable AudioFocusEntry audioFocusEntry, 196 @NonNull List<CarVolumeGroupInfo> mutedVolumeGroups, 197 @NonNull List<AudioFocusEntry> focusHolders, 198 @NonNull List<AudioFocusEntry> focusLosers, 199 int audioZoneId) { 200 this.mAudioFocusRequest = audioFocusEntry; 201 Preconditions.checkArgument(mutedVolumeGroups != null, 202 "Muted volume groups can not be null"); 203 Preconditions.checkArgument(focusHolders != null, 204 "Focus holders can not be null"); 205 Preconditions.checkArgument(focusLosers != null, 206 "Focus losers can not be null"); 207 this.mMutedVolumeGroups = mutedVolumeGroups; 208 this.mFocusHolders = focusHolders; 209 this.mFocusLosers = focusLosers; 210 this.mAudioZoneId = audioZoneId; 211 } 212 213 @Override 214 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 215 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) toString()216 public String toString() { 217 return new StringBuilder().append("OemCarAudioFocusEvaluationRequest {audioZoneId = ") 218 .append(mAudioZoneId).append(", audioFocusInfo = ").append(mAudioFocusRequest) 219 .append(", mutedVolumeGroups = ").append(mMutedVolumeGroups) 220 .append(", focusHolders = ").append(mFocusHolders) 221 .append(", focusLosers = ").append(mFocusLosers) 222 .append(" }").toString(); 223 } 224 225 /** 226 * A builder for {@link OemCarAudioFocusEvaluationRequest} 227 */ 228 @SuppressWarnings("WeakerAccess") 229 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 230 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 231 public static final class Builder { 232 233 private static final int FOCUS_REQUEST_FIELDS_SET = 0x1; 234 private static final int MUTED_VOLUME_GROUPS_FIELDS_SET = 0x2; 235 private static final int FOCUS_HOLDERS_FIELDS_SET = 0x4; 236 private static final int FOCUS_LOSERS_FIELDS_SET = 0x8; 237 private static final int ZONE_ID_FIELDS_SET = 0x10; 238 private static final int BUILDER_USED_FIELDS_SET = 0x20; 239 240 private int mAudioZoneId; 241 private @Nullable AudioFocusEntry mAudioFocusRequest; 242 private @NonNull List<CarVolumeGroupInfo> mMutedVolumeGroups; 243 private @NonNull List<AudioFocusEntry> mFocusHolders; 244 private @NonNull List<AudioFocusEntry> mFocusLosers; 245 246 private long mBuilderFieldsSet = 0L; 247 Builder( @onNull List<CarVolumeGroupInfo> mutedVolumeGroups, @NonNull List<AudioFocusEntry> focusHolders, @NonNull List<AudioFocusEntry> focusLosers, int audioZoneId)248 public Builder( 249 @NonNull List<CarVolumeGroupInfo> mutedVolumeGroups, 250 @NonNull List<AudioFocusEntry> focusHolders, 251 @NonNull List<AudioFocusEntry> focusLosers, 252 int audioZoneId) { 253 Preconditions.checkArgument(mutedVolumeGroups != null, 254 "Muted volume groups can not be null"); 255 Preconditions.checkArgument(focusHolders != null, 256 " Focus holders can not be null"); 257 Preconditions.checkArgument(focusLosers != null, 258 "Focus losers can not be null"); 259 mMutedVolumeGroups = mutedVolumeGroups; 260 mFocusHolders = focusHolders; 261 mFocusLosers = focusLosers; 262 mAudioZoneId = audioZoneId; 263 } 264 265 /** 266 * set the audio zone id 267 */ 268 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 269 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setAudioZoneId(int value)270 public @NonNull Builder setAudioZoneId(int value) { 271 checkNotUsed(); 272 mBuilderFieldsSet |= ZONE_ID_FIELDS_SET; 273 mAudioZoneId = value; 274 return this; 275 } 276 277 /** 278 * Sets the current focus info to evaluate 279 */ 280 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 281 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 282 @NonNull setAudioFocusRequest(@onNull AudioFocusEntry audioFocusRequest)283 public Builder setAudioFocusRequest(@NonNull AudioFocusEntry audioFocusRequest) { 284 Preconditions.checkArgument(audioFocusRequest != null, 285 "Audio focus request can not be null"); 286 checkNotUsed(); 287 mBuilderFieldsSet |= FOCUS_REQUEST_FIELDS_SET; 288 mAudioFocusRequest = audioFocusRequest; 289 return this; 290 } 291 292 /** 293 * Sets the currently muted group volumes 294 */ 295 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 296 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 297 @NonNull setMutedVolumeGroups( @onNull List<CarVolumeGroupInfo> mutedVolumeGroups)298 public Builder setMutedVolumeGroups( 299 @NonNull List<CarVolumeGroupInfo> mutedVolumeGroups) { 300 Preconditions.checkArgument(mutedVolumeGroups != null, 301 "Muted volume groups can not be null"); 302 checkNotUsed(); 303 mBuilderFieldsSet |= MUTED_VOLUME_GROUPS_FIELDS_SET; 304 mMutedVolumeGroups = mutedVolumeGroups; 305 return this; 306 } 307 308 /** @see #setMutedVolumeGroups */ 309 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 310 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) addMutedVolumeGroups(@onNull CarVolumeGroupInfo mutedVolumeGroup)311 public @NonNull Builder addMutedVolumeGroups(@NonNull CarVolumeGroupInfo mutedVolumeGroup) { 312 Preconditions.checkArgument(mutedVolumeGroup != null, 313 "Muted volume group can not be null"); 314 if (mMutedVolumeGroups == null) setMutedVolumeGroups(new ArrayList<>()); 315 mMutedVolumeGroups.add(mutedVolumeGroup); 316 return this; 317 } 318 319 /** 320 * Sets the focus holders 321 */ 322 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 323 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setFocusHolders(@onNull List<AudioFocusEntry> focusHolders)324 public @NonNull Builder setFocusHolders(@NonNull List<AudioFocusEntry> focusHolders) { 325 Preconditions.checkArgument(focusHolders != null, 326 "Focus holders can not be null"); 327 checkNotUsed(); 328 mBuilderFieldsSet |= FOCUS_HOLDERS_FIELDS_SET; 329 mFocusHolders = focusHolders; 330 return this; 331 } 332 333 /** @see #setFocusHolders */ 334 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 335 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) addFocusHolders(@onNull AudioFocusEntry focusHolder)336 public @NonNull Builder addFocusHolders(@NonNull AudioFocusEntry focusHolder) { 337 Preconditions.checkArgument(focusHolder != null, 338 "Focus holder can not be null"); 339 if (mFocusHolders == null) setFocusHolders(new ArrayList<>()); 340 mFocusHolders.add(focusHolder); 341 return this; 342 } 343 344 /** 345 * Sets the focus losers 346 */ 347 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 348 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setFocusLosers(@onNull List<AudioFocusEntry> focusLosers)349 public @NonNull Builder setFocusLosers(@NonNull List<AudioFocusEntry> focusLosers) { 350 Preconditions.checkArgument(focusLosers != null, 351 "Focus losers can not be null"); 352 checkNotUsed(); 353 mBuilderFieldsSet |= FOCUS_LOSERS_FIELDS_SET; 354 mFocusLosers = focusLosers; 355 return this; 356 } 357 358 /** @see #setFocusLosers */ 359 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 360 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) addFocusLosers(@onNull AudioFocusEntry focusLoser)361 public @NonNull Builder addFocusLosers(@NonNull AudioFocusEntry focusLoser) { 362 Preconditions.checkArgument(focusLoser != null, 363 "Focus loser can not be null"); 364 if (mFocusLosers == null) setFocusLosers(new ArrayList<>()); 365 mFocusLosers.add(focusLoser); 366 return this; 367 } 368 369 /** Builds the instance. This builder should not be touched after calling this! */ 370 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 371 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 372 @NonNull build()373 public OemCarAudioFocusEvaluationRequest build() { 374 checkNotUsed(); 375 mBuilderFieldsSet |= BUILDER_USED_FIELDS_SET; // Mark builder used 376 377 OemCarAudioFocusEvaluationRequest o = new OemCarAudioFocusEvaluationRequest( 378 mAudioFocusRequest, 379 mMutedVolumeGroups, 380 mFocusHolders, 381 mFocusLosers, 382 mAudioZoneId); 383 return o; 384 } 385 checkNotUsed()386 private void checkNotUsed() { 387 if ((mBuilderFieldsSet & BUILDER_USED_FIELDS_SET) != 0) { 388 throw new IllegalStateException( 389 "This Builder should not be reused. Use a new Builder instance instead"); 390 } 391 } 392 } 393 safeEquals(Object a, Object b)394 private static boolean safeEquals(Object a, Object b) { 395 return a == b || (a != null && a.equals(b)); 396 } 397 } 398