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.media; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 20 import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SystemApi; 25 import android.car.annotation.ApiRequirements; 26 import android.media.AudioAttributes; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 30 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.util.Preconditions; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Objects; 37 38 /** 39 * Class to encapsulate car volume group information. 40 * 41 * @hide 42 */ 43 @SystemApi 44 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 45 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 46 public final class CarVolumeGroupInfo implements Parcelable { 47 48 private static final long IS_USED_FIELD_SET = 0x01; 49 50 private final String mName; 51 private final int mZoneId; 52 private final int mId; 53 private final int mVolumeGainIndex; 54 private final int mMaxVolumeGainIndex; 55 private final int mMinVolumeGainIndex; 56 private final boolean mIsMuted; 57 private final boolean mIsBlocked; 58 private final boolean mIsAttenuated; 59 private final List<AudioAttributes> mAudioAttributes; 60 CarVolumeGroupInfo( String name, int zoneId, int id, int volumeGainIndex, int maxVolumeGainIndex, int minVolumeGainIndex, boolean isMuted, boolean isBlocked, boolean isAttenuated, List<AudioAttributes> audioAttributes)61 private CarVolumeGroupInfo( 62 String name, 63 int zoneId, 64 int id, 65 int volumeGainIndex, 66 int maxVolumeGainIndex, 67 int minVolumeGainIndex, 68 boolean isMuted, 69 boolean isBlocked, 70 boolean isAttenuated, 71 List<AudioAttributes> audioAttributes) { 72 mName = Objects.requireNonNull(name, "Volume info name can not be null"); 73 mZoneId = zoneId; 74 mId = id; 75 mVolumeGainIndex = volumeGainIndex; 76 mMaxVolumeGainIndex = maxVolumeGainIndex; 77 mMinVolumeGainIndex = minVolumeGainIndex; 78 mIsMuted = isMuted; 79 mIsBlocked = isBlocked; 80 mIsAttenuated = isAttenuated; 81 mAudioAttributes = Objects.requireNonNull(audioAttributes, 82 "Audio attributes can not be null"); 83 } 84 85 /** 86 * Creates volume info from parcel 87 * 88 * @hide 89 */ 90 @VisibleForTesting() CarVolumeGroupInfo(Parcel in)91 public CarVolumeGroupInfo(Parcel in) { 92 int zoneId = in.readInt(); 93 int id = in.readInt(); 94 String name = in.readString(); 95 int volumeGainIndex = in.readInt(); 96 int maxVolumeGainIndex = in.readInt(); 97 int minVolumeGainIndex = in.readInt(); 98 boolean isMuted = in.readBoolean(); 99 boolean isBlocked = in.readBoolean(); 100 boolean isAttenuated = in.readBoolean(); 101 List<AudioAttributes> audioAttributes = new ArrayList<>(); 102 in.readParcelableList(audioAttributes, AudioAttributes.class.getClassLoader(), 103 AudioAttributes.class); 104 this.mZoneId = zoneId; 105 this.mId = id; 106 this.mName = name; 107 this.mVolumeGainIndex = volumeGainIndex; 108 this.mMaxVolumeGainIndex = maxVolumeGainIndex; 109 this.mMinVolumeGainIndex = minVolumeGainIndex; 110 this.mIsMuted = isMuted; 111 this.mIsBlocked = isBlocked; 112 this.mIsAttenuated = isAttenuated; 113 this.mAudioAttributes = audioAttributes; 114 } 115 116 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 117 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 118 @NonNull 119 public static final Creator<CarVolumeGroupInfo> CREATOR = new Creator<>() { 120 @Override 121 @NonNull 122 public CarVolumeGroupInfo createFromParcel(@NonNull Parcel in) { 123 return new CarVolumeGroupInfo(in); 124 } 125 126 @Override 127 @NonNull 128 public CarVolumeGroupInfo[] newArray(int size) { 129 return new CarVolumeGroupInfo[size]; 130 } 131 }; 132 133 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 134 @Override 135 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 136 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) describeContents()137 public int describeContents() { 138 return 0; 139 } 140 141 /** 142 * Returns the volume group name 143 */ 144 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 145 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getName()146 public @NonNull String getName() { 147 return mName; 148 } 149 150 /** 151 * Returns the zone id where the volume group belongs 152 */ 153 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 154 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getZoneId()155 public int getZoneId() { 156 return mZoneId; 157 } 158 159 /** 160 * Returns the volume group id 161 */ 162 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 163 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getId()164 public int getId() { 165 return mId; 166 } 167 168 /** 169 * Returns the volume group volume gain index 170 */ 171 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 172 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getVolumeGainIndex()173 public int getVolumeGainIndex() { 174 return mVolumeGainIndex; 175 } 176 177 /** 178 * Returns the volume group max volume gain index 179 */ 180 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 181 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getMaxVolumeGainIndex()182 public int getMaxVolumeGainIndex() { 183 return mMaxVolumeGainIndex; 184 } 185 186 /** 187 * Returns the volume group min volume gain index 188 */ 189 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 190 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) getMinVolumeGainIndex()191 public int getMinVolumeGainIndex() { 192 return mMinVolumeGainIndex; 193 } 194 195 /** 196 * Returns the volume mute state, {@code true} for muted 197 */ 198 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 199 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) isMuted()200 public boolean isMuted() { 201 return mIsMuted; 202 } 203 204 /** 205 * Returns the volume blocked state, {@code true} for blocked 206 */ 207 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 208 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) isBlocked()209 public boolean isBlocked() { 210 return mIsBlocked; 211 } 212 213 /** 214 * Returns the volume attenuated state, {@code true} for attenuated 215 */ 216 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 217 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) isAttenuated()218 public boolean isAttenuated() { 219 return mIsAttenuated; 220 } 221 222 /** 223 * Returns a list of audio attributes associated with the volume group 224 */ 225 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 226 minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) 227 @NonNull getAudioAttributes()228 public List<AudioAttributes> getAudioAttributes() { 229 assertPlatformVersionAtLeastU(); 230 return mAudioAttributes; 231 } 232 233 @Override toString()234 public String toString() { 235 return new StringBuilder().append("CarVolumeGroupId { .name = ").append(mName) 236 .append(", zone id = ").append(mZoneId).append(" id = ").append(mId) 237 .append(", gain = ").append(mVolumeGainIndex) 238 .append(", max gain = ").append(mMaxVolumeGainIndex) 239 .append(", min gain = ").append(mMinVolumeGainIndex) 240 .append(", muted = ").append(mIsMuted) 241 .append(", blocked = ").append(mIsBlocked) 242 .append(", attenuated = ").append(mIsAttenuated) 243 .append(", audio attributes = ").append(mAudioAttributes) 244 .append(" }").toString(); 245 } 246 247 @Override 248 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 249 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) writeToParcel(@onNull Parcel dest, int flags)250 public void writeToParcel(@NonNull Parcel dest, int flags) { 251 dest.writeInt(mZoneId); 252 dest.writeInt(mId); 253 dest.writeString(mName); 254 dest.writeInt(mVolumeGainIndex); 255 dest.writeInt(mMaxVolumeGainIndex); 256 dest.writeInt(mMinVolumeGainIndex); 257 dest.writeBoolean(mIsMuted); 258 dest.writeBoolean(mIsBlocked); 259 dest.writeBoolean(mIsAttenuated); 260 dest.writeParcelableList(mAudioAttributes, flags); 261 } 262 263 /** 264 * Determines if it is the same volume group, only comparing the group name, zone id, and 265 * group id. 266 * 267 * @return {@code true} if the group info is the same, {@code false} otherwise 268 */ 269 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 270 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) isSameVolumeGroup(@ullable CarVolumeGroupInfo group)271 public boolean isSameVolumeGroup(@Nullable CarVolumeGroupInfo group) { 272 return group != null && mZoneId == group.mZoneId && mId == group.mId 273 && mName.equals(group.mName); 274 } 275 276 @Override equals(Object o)277 public boolean equals(Object o) { 278 if (this == o) { 279 return true; 280 } 281 282 if (!(o instanceof CarVolumeGroupInfo)) { 283 return false; 284 } 285 286 CarVolumeGroupInfo that = (CarVolumeGroupInfo) o; 287 288 return isSameVolumeGroup(that) && mVolumeGainIndex == that.mVolumeGainIndex 289 && mMaxVolumeGainIndex == that.mMaxVolumeGainIndex 290 && mMinVolumeGainIndex == that.mMinVolumeGainIndex 291 && mIsMuted == that.mIsMuted && mIsBlocked == that.mIsBlocked 292 && mIsAttenuated == that.mIsAttenuated 293 && Objects.equals(mAudioAttributes, that.mAudioAttributes); 294 } 295 296 @Override hashCode()297 public int hashCode() { 298 return Objects.hash(mName, mZoneId, mId, mVolumeGainIndex, mMaxVolumeGainIndex, 299 mMinVolumeGainIndex, mIsMuted, mIsBlocked, mIsAttenuated, mAudioAttributes); 300 } 301 302 /** 303 * A builder for {@link CarVolumeGroupInfo} 304 */ 305 @SuppressWarnings("WeakerAccess") 306 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 307 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 308 public static final class Builder { 309 310 private @NonNull String mName; 311 private int mZoneId; 312 private int mId; 313 private int mVolumeGainIndex; 314 private int mMinVolumeGainIndex; 315 private int mMaxVolumeGainIndex; 316 private boolean mIsMuted; 317 private boolean mIsBlocked; 318 private boolean mIsAttenuated; 319 private List<AudioAttributes> mAudioAttributes = new ArrayList<>(); 320 321 private long mBuilderFieldsSet = 0L; 322 Builder(@onNull String name, int zoneId, int id)323 public Builder(@NonNull String name, int zoneId, int id) { 324 mName = Objects.requireNonNull(name, "Volume info name can not be null"); 325 mZoneId = zoneId; 326 mId = id; 327 } 328 Builder(@onNull CarVolumeGroupInfo info)329 public Builder(@NonNull CarVolumeGroupInfo info) { 330 Objects.requireNonNull(info, "Volume info can not be null"); 331 mName = info.mName; 332 mZoneId = info.mZoneId; 333 mId = info.mId; 334 mVolumeGainIndex = info.mVolumeGainIndex; 335 mMaxVolumeGainIndex = info.mMaxVolumeGainIndex; 336 mMinVolumeGainIndex = info.mMinVolumeGainIndex; 337 mIsMuted = info.mIsMuted; 338 mIsBlocked = info.mIsBlocked; 339 mIsAttenuated = info.mIsAttenuated; 340 mAudioAttributes = info.mAudioAttributes; 341 } 342 343 /** 344 * Sets the volume group volume gain index 345 */ 346 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 347 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setVolumeGainIndex(int gainIndex)348 public @NonNull Builder setVolumeGainIndex(int gainIndex) { 349 checkNotUsed(); 350 mVolumeGainIndex = gainIndex; 351 return this; 352 } 353 354 /** 355 * Sets the volume group max volume gain index 356 */ 357 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 358 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setMaxVolumeGainIndex(int gainIndex)359 public @NonNull Builder setMaxVolumeGainIndex(int gainIndex) { 360 checkNotUsed(); 361 mMaxVolumeGainIndex = gainIndex; 362 return this; 363 } 364 365 /** 366 * Sets the volume group min volume gain index 367 */ 368 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 369 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setMinVolumeGainIndex(int gainIndex)370 public @NonNull Builder setMinVolumeGainIndex(int gainIndex) { 371 checkNotUsed(); 372 mMinVolumeGainIndex = gainIndex; 373 return this; 374 } 375 376 /** 377 * Sets the volume group muted state, {@code true} for muted 378 */ 379 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 380 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setMuted(boolean muted)381 public @NonNull Builder setMuted(boolean muted) { 382 checkNotUsed(); 383 mIsMuted = muted; 384 return this; 385 } 386 387 /** 388 * Sets the volume group blocked state, {@code true} for blocked 389 */ 390 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 391 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setBlocked(boolean blocked)392 public @NonNull Builder setBlocked(boolean blocked) { 393 checkNotUsed(); 394 mIsBlocked = blocked; 395 return this; 396 } 397 398 /** 399 * Sets the volume group attenuated state, {@code true} for attenuated 400 */ 401 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 402 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) setAttenuated(boolean attenuated)403 public @NonNull Builder setAttenuated(boolean attenuated) { 404 checkNotUsed(); 405 mIsAttenuated = attenuated; 406 return this; 407 } 408 409 /** 410 * Sets the list of audio attributes associated with the volume group 411 */ 412 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 413 minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) 414 @NonNull setAudioAttributes(@onNull List<AudioAttributes> audioAttributes)415 public Builder setAudioAttributes(@NonNull List<AudioAttributes> audioAttributes) { 416 // TODO(b/273843708): add assertion back. getOccupantZoneId is not version guarded 417 // properly when it is used within Car module. Assertion should be added backed once 418 // b/280702422 is resolved 419 // assertPlatformVersionAtLeastU(); 420 checkNotUsed(); 421 mAudioAttributes = Objects.requireNonNull(audioAttributes, 422 "Audio Attributes can not be null"); 423 return this; 424 } 425 426 /** 427 * Builds the instance. 428 * 429 * @throws IllegalArgumentException if min volume gain index is larger than max volume 430 * gain index, or if the volume gain index is outside the range of max and min volume 431 * gain index. 432 * 433 * @throws IllegalStateException if the constructor is re-used 434 */ 435 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 436 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 437 @NonNull build()438 public CarVolumeGroupInfo build() { 439 checkNotUsed(); 440 validateGainIndexRange(); 441 442 mBuilderFieldsSet |= IS_USED_FIELD_SET; // Mark builder used 443 444 445 return new CarVolumeGroupInfo(mName, mZoneId, mId, mVolumeGainIndex, 446 mMaxVolumeGainIndex, mMinVolumeGainIndex, mIsMuted, mIsBlocked, mIsAttenuated, 447 mAudioAttributes); 448 } 449 validateGainIndexRange()450 private void validateGainIndexRange() { 451 Preconditions.checkArgument(mMinVolumeGainIndex < mMaxVolumeGainIndex, 452 "Min volume gain index %d must be smaller than max volume gain index %d", 453 mMinVolumeGainIndex, mMaxVolumeGainIndex); 454 455 Preconditions.checkArgumentInRange(mVolumeGainIndex, mMinVolumeGainIndex, 456 mMaxVolumeGainIndex, "Volume gain index"); 457 } 458 459 private void checkNotUsed() throws IllegalStateException { 460 if ((mBuilderFieldsSet & IS_USED_FIELD_SET) != 0) { 461 throw new IllegalStateException( 462 "This Builder should not be reused. Use a new Builder instance instead"); 463 } 464 } 465 } 466 } 467