1 /* 2 * Copyright (C) 2024 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.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.SystemApi; 23 import android.car.feature.Flags; 24 import android.os.Parcelable; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.util.Objects; 31 32 /** 33 * Class to encapsulate the features available for the car audio system and should be considered 34 * by the OEM car audio service. 35 * 36 * @hide 37 */ 38 @SystemApi 39 @FlaggedApi(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES) 40 public final class CarAudioFeaturesInfo implements Parcelable { 41 42 /** 43 * Used to indicate that no feature is supported by the audio system 44 * 45 * <p><b>Note</b> Only use internally to create a feature object with no features available. 46 * 47 * @hide 48 */ 49 public static final int AUDIO_FEATURE_NO_FEATURE = 0x0; 50 51 /** 52 * Can be used to determined if the audio focus request for playback on dynamic devices should 53 * be treated independently from the rest of the car audio zone. If the feature is enabled, 54 * focus requests should only interact if the focus requests are for the same zone and 55 * targeting playback will be routed to the same device. Otherwise, the focus requests should 56 * interact only if the requests are for the same audio zone. 57 */ 58 public static final int AUDIO_FEATURE_ISOLATED_DEVICE_FOCUS = 0x1 << 0; 59 60 /** 61 * Can be used to determine if fade manager configurations are supported. If the feature is 62 * available, transient fade manager configurations can be provided as part of the focus request 63 * result. These shall be used to fade players whose focus state changed as part of the focus 64 * evaluation. If the feature is not available, any provided fade manager configurations will 65 * be ignored. 66 */ 67 public static final int AUDIO_FEATURE_FADE_MANAGER_CONFIGS = 0x1 << 1; 68 69 /** @hide */ 70 @IntDef(flag = true, prefix = "AUDIO_FEATURE", value = { 71 AUDIO_FEATURE_NO_FEATURE, 72 AUDIO_FEATURE_ISOLATED_DEVICE_FOCUS, 73 AUDIO_FEATURE_FADE_MANAGER_CONFIGS, 74 }) 75 @Retention(RetentionPolicy.SOURCE) 76 public @interface AudioFeature {} 77 78 private final int mCarAudioFeatures; 79 CarAudioFeaturesInfo(int carAudioFeatures)80 CarAudioFeaturesInfo(int carAudioFeatures) { 81 mCarAudioFeatures = checkFeatures(carAudioFeatures); 82 } 83 84 /** 85 * Determines if a particular audio feature is enabled and should be supported 86 * 87 * @param feature Feature to query, can be {@link #AUDIO_FEATURE_ISOLATED_DEVICE_FOCUS} 88 * or {@link #AUDIO_FEATURE_FADE_MANAGER_CONFIGS} 89 * @return {@code true} if the feature is enabled, {@code false} otherwise 90 */ isAudioFeatureEnabled(@udioFeature int feature)91 public boolean isAudioFeatureEnabled(@AudioFeature int feature) { 92 return (mCarAudioFeatures & feature) == feature; 93 } 94 95 @Override toString()96 public String toString() { 97 return "CarAudioFeaturesInfo { features = " + featuresToString(mCarAudioFeatures) + " }"; 98 } 99 100 @Override writeToParcel(@onNull android.os.Parcel dest, int flags)101 public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { 102 dest.writeInt(mCarAudioFeatures); 103 } 104 105 @Override describeContents()106 public int describeContents() { 107 return 0; 108 } 109 110 /** @hide */ 111 @VisibleForTesting CarAudioFeaturesInfo(@onNull android.os.Parcel in)112 public CarAudioFeaturesInfo(@NonNull android.os.Parcel in) { 113 mCarAudioFeatures = in.readInt(); 114 } 115 116 @NonNull 117 public static final Creator<CarAudioFeaturesInfo> CREATOR = new Creator<>() { 118 @Override 119 public CarAudioFeaturesInfo[] newArray(int size) { 120 return new CarAudioFeaturesInfo[size]; 121 } 122 123 @Override 124 public CarAudioFeaturesInfo createFromParcel(@NonNull android.os.Parcel in) { 125 return new CarAudioFeaturesInfo(in); 126 } 127 }; 128 129 @Override equals(Object o)130 public boolean equals(Object o) { 131 if (this == o) { 132 return true; 133 } 134 135 if (!(o instanceof CarAudioFeaturesInfo)) { 136 return false; 137 } 138 139 CarAudioFeaturesInfo that = (CarAudioFeaturesInfo) o; 140 return mCarAudioFeatures == that.mCarAudioFeatures; 141 } 142 143 @Override hashCode()144 public int hashCode() { 145 return Objects.hash(mCarAudioFeatures); 146 } 147 featuresToString(int features)148 private static String featuresToString(int features) { 149 if (features == AUDIO_FEATURE_NO_FEATURE) { 150 return "NONE"; 151 } 152 StringBuilder builder = new StringBuilder(); 153 if (checkFeature(features, AUDIO_FEATURE_ISOLATED_DEVICE_FOCUS)) { 154 builder.append("ISOLATED_DEVICE_FOCUS"); 155 } 156 if (checkFeature(features, AUDIO_FEATURE_FADE_MANAGER_CONFIGS)) { 157 builder.append(builder.isEmpty() ? "" : "|"); 158 builder.append("FADE_MANAGER_CONFIGS"); 159 } 160 return builder.toString(); 161 } 162 checkFeatures(int carAudioFeatures)163 private static int checkFeatures(int carAudioFeatures) { 164 if (carAudioFeatures == AUDIO_FEATURE_NO_FEATURE) { 165 return carAudioFeatures; 166 } 167 int tempFeatures = carAudioFeatures; 168 tempFeatures = tempFeatures & ~AUDIO_FEATURE_ISOLATED_DEVICE_FOCUS; 169 tempFeatures = tempFeatures & ~AUDIO_FEATURE_FADE_MANAGER_CONFIGS; 170 if (tempFeatures != 0) { 171 throw new IllegalArgumentException("Car audio features " + tempFeatures 172 + " are invalid"); 173 } 174 return carAudioFeatures; 175 } 176 checkFeature(int feature)177 private static int checkFeature(int feature) { 178 if (feature == AUDIO_FEATURE_NO_FEATURE) { 179 return feature; 180 } 181 if (checkFeature(feature, AUDIO_FEATURE_ISOLATED_DEVICE_FOCUS)) { 182 return feature; 183 } 184 if (checkFeature(feature, AUDIO_FEATURE_FADE_MANAGER_CONFIGS)) { 185 return feature; 186 } 187 throw new IllegalArgumentException("Car audio feature " + feature + " is invalid"); 188 } 189 checkFeature(int feature, int checkFeature)190 private static boolean checkFeature(int feature, int checkFeature) { 191 return (feature & checkFeature) == checkFeature; 192 } 193 194 /** 195 * A builder for {@link CarAudioFeaturesInfo} 196 * 197 * @hide 198 */ 199 public static final class Builder { 200 201 private boolean mBuilderUsed = false; 202 private int mAudioFeatures; 203 Builder(@onNull CarAudioFeaturesInfo entry)204 public Builder(@NonNull CarAudioFeaturesInfo entry) { 205 this(entry.mCarAudioFeatures); 206 } 207 Builder(int audioFeatures)208 public Builder(int audioFeatures) { 209 mAudioFeatures = checkFeatures(audioFeatures); 210 } 211 212 /** 213 * Adds an audio feature to the audio features info 214 * 215 * @param feature Feature to append, can be {@link #AUDIO_FEATURE_ISOLATED_DEVICE_FOCUS} 216 * or {@link #AUDIO_FEATURE_FADE_MANAGER_CONFIGS} 217 * @throws IllegalArgumentException if the feature parameter is not a supported feature 218 * @throws IllegalStateException if the builder is re-used after calling {@link #build()} 219 */ addAudioFeature(int feature)220 public @NonNull Builder addAudioFeature(int feature) { 221 checkNotUsed(); 222 mAudioFeatures |= checkFeature(feature); 223 return this; 224 } 225 226 /** Builds the instance. This builder should not be touched after calling this! */ build()227 public @NonNull CarAudioFeaturesInfo build() { 228 checkNotUsed(); 229 mBuilderUsed = true; 230 return new CarAudioFeaturesInfo(mAudioFeatures); 231 } 232 checkNotUsed()233 private void checkNotUsed() { 234 if (mBuilderUsed) { 235 throw new IllegalStateException( 236 "This Builder should not be reused. Use a new Builder instance instead"); 237 } 238 } 239 } 240 } 241