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 equals(Object o)162 public boolean equals(Object o) { 163 if (this == o) { 164 return true; 165 } 166 167 if (!(o instanceof OemCarAudioFocusEvaluationRequest)) { 168 return false; 169 } 170 171 OemCarAudioFocusEvaluationRequest that = (OemCarAudioFocusEvaluationRequest) o; 172 173 return safeEquals(mAudioFocusRequest, that.mAudioFocusRequest) 174 && mFocusHolders.equals(that.mFocusHolders) 175 && mFocusLosers.equals(that.mFocusLosers) 176 && mMutedVolumeGroups.equals(that.mMutedVolumeGroups) 177 && mAudioZoneId == that.mAudioZoneId; 178 } 179 180 @Override hashCode()181 public int hashCode() { 182 return Objects.hash(mAudioFocusRequest, mFocusHolders, mFocusLosers, mMutedVolumeGroups, 183 mAudioZoneId); 184 } 185 186 /** 187 * @hide 188 */ 189 @VisibleForTesting OemCarAudioFocusEvaluationRequest( @ullable AudioFocusEntry audioFocusEntry, @NonNull List<CarVolumeGroupInfo> mutedVolumeGroups, @NonNull List<AudioFocusEntry> focusHolders, @NonNull List<AudioFocusEntry> focusLosers, int audioZoneId)190 public OemCarAudioFocusEvaluationRequest( 191 @Nullable AudioFocusEntry audioFocusEntry, 192 @NonNull List<CarVolumeGroupInfo> mutedVolumeGroups, 193 @NonNull List<AudioFocusEntry> focusHolders, 194 @NonNull List<AudioFocusEntry> focusLosers, 195 int audioZoneId) { 196 this.mAudioFocusRequest = audioFocusEntry; 197 Preconditions.checkArgument(mutedVolumeGroups != null, 198 "Muted volume groups can not be null"); 199 Preconditions.checkArgument(focusHolders != null, 200 "Focus holders can not be null"); 201 Preconditions.checkArgument(focusLosers != null, 202 "Focus losers can not be null"); 203 this.mMutedVolumeGroups = mutedVolumeGroups; 204 this.mFocusHolders = focusHolders; 205 this.mFocusLosers = focusLosers; 206 this.mAudioZoneId = audioZoneId; 207 } 208 209 @Override toString()210 public String toString() { 211 return new StringBuilder().append("OemCarAudioFocusEvaluationRequest {audioZoneId = ") 212 .append(mAudioZoneId).append(", audioFocusInfo = ").append(mAudioFocusRequest) 213 .append(", mutedVolumeGroups = ").append(mMutedVolumeGroups) 214 .append(", focusHolders = ").append(mFocusHolders) 215 .append(", focusLosers = ").append(mFocusLosers) 216 .append(" }").toString(); 217 } 218 219 /** 220 * A builder for {@link OemCarAudioFocusEvaluationRequest} 221 */ 222 @SuppressWarnings("WeakerAccess") 223 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 224 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 225 public static final class Builder { 226 227 private static final int FOCUS_REQUEST_FIELDS_SET = 0x1; 228 private static final int MUTED_VOLUME_GROUPS_FIELDS_SET = 0x2; 229 private static final int FOCUS_HOLDERS_FIELDS_SET = 0x4; 230 private static final int FOCUS_LOSERS_FIELDS_SET = 0x8; 231 private static final int ZONE_ID_FIELDS_SET = 0x10; 232 private static final int BUILDER_USED_FIELDS_SET = 0x20; 233 234 private int mAudioZoneId; 235 private @Nullable AudioFocusEntry mAudioFocusRequest; 236 private @NonNull List<CarVolumeGroupInfo> mMutedVolumeGroups; 237 private @NonNull List<AudioFocusEntry> mFocusHolders; 238 private @NonNull List<AudioFocusEntry> mFocusLosers; 239 240 private long mBuilderFieldsSet = 0L; 241 Builder( @onNull List<CarVolumeGroupInfo> mutedVolumeGroups, @NonNull List<AudioFocusEntry> focusHolders, @NonNull List<AudioFocusEntry> focusLosers, int audioZoneId)242 public Builder( 243 @NonNull List<CarVolumeGroupInfo> mutedVolumeGroups, 244 @NonNull List<AudioFocusEntry> focusHolders, 245 @NonNull List<AudioFocusEntry> focusLosers, 246 int audioZoneId) { 247 Preconditions.checkArgument(mutedVolumeGroups != null, 248 "Muted volume groups can not be null"); 249 Preconditions.checkArgument(focusHolders != null, 250 " Focus holders can not be null"); 251 Preconditions.checkArgument(focusLosers != null, 252 "Focus losers can not be null"); 253 mMutedVolumeGroups = mutedVolumeGroups; 254 mFocusHolders = focusHolders; 255 mFocusLosers = focusLosers; 256 mAudioZoneId = audioZoneId; 257 } 258 259 /** 260 * set the audio zone id 261 */ 262 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 263 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setAudioZoneId(int value)264 public @NonNull Builder setAudioZoneId(int value) { 265 checkNotUsed(); 266 mBuilderFieldsSet |= ZONE_ID_FIELDS_SET; 267 mAudioZoneId = value; 268 return this; 269 } 270 271 /** 272 * Sets the current focus info to evaluate 273 */ 274 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 275 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 276 @NonNull setAudioFocusRequest(@onNull AudioFocusEntry audioFocusRequest)277 public Builder setAudioFocusRequest(@NonNull AudioFocusEntry audioFocusRequest) { 278 Preconditions.checkArgument(audioFocusRequest != null, 279 "Audio focus request can not be null"); 280 checkNotUsed(); 281 mBuilderFieldsSet |= FOCUS_REQUEST_FIELDS_SET; 282 mAudioFocusRequest = audioFocusRequest; 283 return this; 284 } 285 286 /** 287 * Sets the currently muted group volumes 288 */ 289 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 290 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 291 @NonNull setMutedVolumeGroups( @onNull List<CarVolumeGroupInfo> mutedVolumeGroups)292 public Builder setMutedVolumeGroups( 293 @NonNull List<CarVolumeGroupInfo> mutedVolumeGroups) { 294 Preconditions.checkArgument(mutedVolumeGroups != null, 295 "Muted volume groups can not be null"); 296 checkNotUsed(); 297 mBuilderFieldsSet |= MUTED_VOLUME_GROUPS_FIELDS_SET; 298 mMutedVolumeGroups = mutedVolumeGroups; 299 return this; 300 } 301 302 /** @see #setMutedVolumeGroups */ 303 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 304 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) addMutedVolumeGroups(@onNull CarVolumeGroupInfo mutedVolumeGroup)305 public @NonNull Builder addMutedVolumeGroups(@NonNull CarVolumeGroupInfo mutedVolumeGroup) { 306 Preconditions.checkArgument(mutedVolumeGroup != null, 307 "Muted volume group can not be null"); 308 if (mMutedVolumeGroups == null) setMutedVolumeGroups(new ArrayList<>()); 309 mMutedVolumeGroups.add(mutedVolumeGroup); 310 return this; 311 } 312 313 /** 314 * Sets the focus holders 315 */ 316 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 317 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setFocusHolders(@onNull List<AudioFocusEntry> focusHolders)318 public @NonNull Builder setFocusHolders(@NonNull List<AudioFocusEntry> focusHolders) { 319 Preconditions.checkArgument(focusHolders != null, 320 "Focus holders can not be null"); 321 checkNotUsed(); 322 mBuilderFieldsSet |= FOCUS_HOLDERS_FIELDS_SET; 323 mFocusHolders = focusHolders; 324 return this; 325 } 326 327 /** @see #setFocusHolders */ 328 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 329 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) addFocusHolders(@onNull AudioFocusEntry focusHolder)330 public @NonNull Builder addFocusHolders(@NonNull AudioFocusEntry focusHolder) { 331 Preconditions.checkArgument(focusHolder != null, 332 "Focus holder can not be null"); 333 if (mFocusHolders == null) setFocusHolders(new ArrayList<>()); 334 mFocusHolders.add(focusHolder); 335 return this; 336 } 337 338 /** 339 * Sets the focus losers 340 */ 341 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 342 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setFocusLosers(@onNull List<AudioFocusEntry> focusLosers)343 public @NonNull Builder setFocusLosers(@NonNull List<AudioFocusEntry> focusLosers) { 344 Preconditions.checkArgument(focusLosers != null, 345 "Focus losers can not be null"); 346 checkNotUsed(); 347 mBuilderFieldsSet |= FOCUS_LOSERS_FIELDS_SET; 348 mFocusLosers = focusLosers; 349 return this; 350 } 351 352 /** @see #setFocusLosers */ 353 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 354 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) addFocusLosers(@onNull AudioFocusEntry focusLoser)355 public @NonNull Builder addFocusLosers(@NonNull AudioFocusEntry focusLoser) { 356 Preconditions.checkArgument(focusLoser != null, 357 "Focus loser can not be null"); 358 if (mFocusLosers == null) setFocusLosers(new ArrayList<>()); 359 mFocusLosers.add(focusLoser); 360 return this; 361 } 362 363 /** Builds the instance. This builder should not be touched after calling this! */ 364 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 365 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 366 @NonNull build()367 public OemCarAudioFocusEvaluationRequest build() { 368 checkNotUsed(); 369 mBuilderFieldsSet |= BUILDER_USED_FIELDS_SET; // Mark builder used 370 371 OemCarAudioFocusEvaluationRequest o = new OemCarAudioFocusEvaluationRequest( 372 mAudioFocusRequest, 373 mMutedVolumeGroups, 374 mFocusHolders, 375 mFocusLosers, 376 mAudioZoneId); 377 return o; 378 } 379 checkNotUsed()380 private void checkNotUsed() { 381 if ((mBuilderFieldsSet & BUILDER_USED_FIELDS_SET) != 0) { 382 throw new IllegalStateException( 383 "This Builder should not be reused. Use a new Builder instance instead"); 384 } 385 } 386 } 387 safeEquals(Object a, Object b)388 private static boolean safeEquals(Object a, Object b) { 389 return a == b || (a != null && a.equals(b)); 390 } 391 } 392