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 android.annotation.NonNull; 20 import android.annotation.SystemApi; 21 import android.media.AudioFocusInfo; 22 import android.os.Parcelable; 23 24 import com.android.internal.annotations.VisibleForTesting; 25 26 import java.util.Objects; 27 28 /** 29 * Class to encapsulate the focus information of evaluation from a car oem audio focus service 30 * 31 * @hide 32 */ 33 @SystemApi 34 public final class AudioFocusEntry implements Parcelable { 35 36 @NonNull 37 private final AudioFocusInfo mAudioFocusInfo; 38 private final int mAudioContextId; 39 private final int mAudioVolumeGroupId; 40 private final int mAudioFocusResult; 41 AudioFocusEntry( @onNull AudioFocusInfo audioFocusInfo, int audioContextId, int audioVolumeGroupId, int focusResult)42 AudioFocusEntry( 43 @NonNull AudioFocusInfo audioFocusInfo, 44 int audioContextId, 45 int audioVolumeGroupId, 46 int focusResult) { 47 mAudioFocusInfo = Objects.requireNonNull(audioFocusInfo, 48 "Audio focus info can not be null"); 49 mAudioContextId = audioContextId; 50 mAudioVolumeGroupId = audioVolumeGroupId; 51 mAudioFocusResult = focusResult; 52 } 53 54 /** 55 * Returns the audio focus info 56 */ getAudioFocusInfo()57 public @NonNull AudioFocusInfo getAudioFocusInfo() { 58 return mAudioFocusInfo; 59 } 60 61 /** 62 * Returns the caudio context as evaluated from the audio attributes 63 */ getAudioContextId()64 public int getAudioContextId() { 65 return mAudioContextId; 66 } 67 68 /** 69 * Returns the volume group as evaluated from the audio attributes 70 */ getAudioVolumeGroupId()71 public int getAudioVolumeGroupId() { 72 return mAudioVolumeGroupId; 73 } 74 75 /** 76 * Returns the focus results, must be on of {@link AudioManager.AUDIOFOCUS_GAIN}, 77 * {@link AudioManager.AUDIOFOCUS_LOSS}, {@link AudioManager.AUDIOFOCUS_LOSS_TRANSIENT}, 78 * {@link AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} 79 **/ getAudioFocusResult()80 public int getAudioFocusResult() { 81 return mAudioFocusResult; 82 } 83 84 @Override toString()85 public String toString() { 86 return new StringBuilder().append("AudioFocusEntry { audioFocusInfo = ") 87 .append(getAudioFocusInfoString()).append(", audioContextId = ") 88 .append(mAudioContextId).append(", audioVolumeGroupId = ") 89 .append(mAudioVolumeGroupId).append(", focusResult = ") 90 .append(mAudioFocusResult).append(" }").toString(); 91 } 92 getAudioFocusInfoString()93 private String getAudioFocusInfoString() { 94 return new StringBuilder().append("{ attributes: ").append(mAudioFocusInfo.getAttributes()) 95 .append(", UID : ").append(mAudioFocusInfo.getClientUid()) 96 .append(", client Id: ").append(mAudioFocusInfo.getClientId()) 97 .append(", pkg: ").append(mAudioFocusInfo.getPackageName()) 98 .append(", gain: ").append(mAudioFocusInfo.getGainRequest()) 99 .append(", loss received: ").append(mAudioFocusInfo.getLossReceived()) 100 .append(", flags: ").append(mAudioFocusInfo.getFlags()) 101 .append("}").toString(); 102 } 103 104 @Override writeToParcel(@onNull android.os.Parcel dest, int flags)105 public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { 106 mAudioFocusInfo.writeToParcel(dest, flags); 107 dest.writeInt(mAudioContextId); 108 dest.writeInt(mAudioVolumeGroupId); 109 dest.writeInt(mAudioFocusResult); 110 } 111 112 @Override describeContents()113 public int describeContents() { 114 return 0; 115 } 116 117 /** @hide */ 118 @SuppressWarnings({"unchecked", "RedundantCast"}) 119 @VisibleForTesting AudioFocusEntry(@onNull android.os.Parcel in)120 public AudioFocusEntry(@NonNull android.os.Parcel in) { 121 AudioFocusInfo audioFocusInfo = AudioFocusInfo.CREATOR.createFromParcel(in); 122 int audioContextId = in.readInt(); 123 int audioVolumeGroupId = in.readInt(); 124 int focusResult = in.readInt(); 125 126 mAudioFocusInfo = audioFocusInfo; 127 mAudioContextId = audioContextId; 128 mAudioVolumeGroupId = audioVolumeGroupId; 129 mAudioFocusResult = focusResult; 130 } 131 132 @NonNull 133 public static final Parcelable.Creator<AudioFocusEntry> CREATOR = 134 new Parcelable.Creator<>() { 135 @Override 136 public AudioFocusEntry[] newArray(int size) { 137 return new AudioFocusEntry[size]; 138 } 139 140 @Override 141 public AudioFocusEntry createFromParcel(@NonNull android.os.Parcel in) { 142 return new AudioFocusEntry(in); 143 } 144 }; 145 146 @Override equals(Object o)147 public boolean equals(Object o) { 148 if (this == o) { 149 return true; 150 } 151 152 if (!(o instanceof AudioFocusEntry)) { 153 return false; 154 } 155 156 AudioFocusEntry that = (AudioFocusEntry) o; 157 158 return mAudioContextId == that.mAudioContextId 159 && mAudioFocusResult == that.mAudioFocusResult 160 && mAudioVolumeGroupId == that.mAudioVolumeGroupId 161 && mAudioFocusInfo.equals(that.mAudioFocusInfo); 162 } 163 164 @Override hashCode()165 public int hashCode() { 166 return Objects.hash(mAudioFocusInfo.hashCode(), mAudioContextId, mAudioFocusResult, 167 mAudioVolumeGroupId); 168 } 169 170 /** 171 * A builder for {@link AudioFocusEntry} 172 */ 173 @SuppressWarnings("WeakerAccess") 174 public static final class Builder { 175 176 private @NonNull AudioFocusInfo mAudioFocusInfo; 177 private int mAudioContextId; 178 private int mAudioVolumeGroupId; 179 private int mAudioFocusResult; 180 181 private long mBuilderFieldsSet = 0L; 182 Builder(@onNull AudioFocusEntry entry)183 public Builder(@NonNull AudioFocusEntry entry) { 184 this(Objects.requireNonNull(entry, "Audio focus entry can not be null") 185 .mAudioFocusInfo, entry.mAudioContextId, entry.mAudioVolumeGroupId, 186 entry.mAudioFocusResult); 187 } 188 Builder( @onNull AudioFocusInfo audioFocusInfo, int audioContextId, int audioVolumeGroupId, int focusResult)189 public Builder( 190 @NonNull AudioFocusInfo audioFocusInfo, 191 int audioContextId, 192 int audioVolumeGroupId, 193 int focusResult) { 194 mAudioFocusInfo = Objects.requireNonNull(audioFocusInfo, 195 "Audio focus info can not be null"); 196 mAudioContextId = audioContextId; 197 mAudioVolumeGroupId = audioVolumeGroupId; 198 mAudioFocusResult = focusResult; 199 } 200 201 /** see {@link AudioFocusEntry#getAudioFocusInfo()} */ setAudioFocusInfo(@onNull AudioFocusInfo audioFocusInfo)202 public @NonNull Builder setAudioFocusInfo(@NonNull AudioFocusInfo audioFocusInfo) { 203 checkNotUsed(); 204 mBuilderFieldsSet |= 0x1; 205 mAudioFocusInfo = Objects.requireNonNull(audioFocusInfo, 206 "Audio focus info can not be null"); 207 return this; 208 } 209 210 /** see {@link AudioFocusEntry#getAudioContextId()} */ setAudioContextId(int value)211 public @NonNull Builder setAudioContextId(int value) { 212 checkNotUsed(); 213 mBuilderFieldsSet |= 0x2; 214 mAudioContextId = value; 215 return this; 216 } 217 218 /** see {@link AudioFocusEntry#getAudioVolumeGroupId()} */ setAudioVolumeGroupId(int value)219 public @NonNull Builder setAudioVolumeGroupId(int value) { 220 checkNotUsed(); 221 mBuilderFieldsSet |= 0x4; 222 mAudioVolumeGroupId = value; 223 return this; 224 } 225 226 /** see {@link AudioFocusEntry#getAudioFocusResult()} */ setAudioFocusResult(int value)227 public @NonNull Builder setAudioFocusResult(int value) { 228 checkNotUsed(); 229 mBuilderFieldsSet |= 0x8; 230 mAudioFocusResult = value; 231 return this; 232 } 233 234 /** Builds the instance. This builder should not be touched after calling this! */ build()235 public @NonNull AudioFocusEntry build() { 236 checkNotUsed(); 237 mBuilderFieldsSet |= 0x10; // Mark builder used 238 239 AudioFocusEntry o = new AudioFocusEntry( 240 mAudioFocusInfo, 241 mAudioContextId, 242 mAudioVolumeGroupId, 243 mAudioFocusResult); 244 return o; 245 } 246 checkNotUsed()247 private void checkNotUsed() { 248 if ((mBuilderFieldsSet & 0x10) != 0) { 249 throw new IllegalStateException( 250 "This Builder should not be reused. Use a new Builder instance instead"); 251 } 252 } 253 } 254 } 255